Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d5c402ff6 |
@@ -4,3 +4,4 @@
|
||||
**/node_modules
|
||||
**/target
|
||||
dist
|
||||
documentation
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
nym-validator-rewarder/.sqlx/** diff=nodiff
|
||||
@@ -6,27 +6,20 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arc-ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: documentation/docs
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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 git python3 && sudo apt-get update --fix-missing
|
||||
- name: Install pip3
|
||||
run: sudo apt install -y python3-pip
|
||||
run: sudo apt install -y python3-pip
|
||||
- name: Install Python3 modules
|
||||
run: sudo pip3 install pandas tabulate
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
with:
|
||||
version: 9
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -36,13 +29,33 @@ jobs:
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
- name: Install mdbook and plugins
|
||||
run: cd documentation && ./install_mdbook_deps.sh
|
||||
- name: Remove existing Nym config directory (`~/.nym/`)
|
||||
run: cd documentation && ./remove_existing_config.sh
|
||||
continue-on-error: false
|
||||
# This is the original flow
|
||||
# - name: Build all projects in documentation/ & move to ~/dist/docs/
|
||||
# run: cd documentation && ./build_all_to_dist.sh
|
||||
|
||||
- name: Install project dependencies
|
||||
run: pnpm i
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
- name: Move files to /dist/
|
||||
run: ../scripts/move-to-dist.sh
|
||||
# This is a workaround replacement which builds on the last working commit b332a6b55668f60988e36961f3f62a794ba82ddb and then on current branch
|
||||
- name: Save current branch to ~/current_branch
|
||||
run: git rev-parse --abbrev-ref HEAD > ~/current_branch
|
||||
- name: Git pull, reset & switch to b332a6b55668f60988e36961f3f62a794ba82ddb
|
||||
run: git pull && git reset --hard && git checkout b332a6b55668f60988e36961f3f62a794ba82ddb
|
||||
- name: Build all projects in documentation/ & move to ~/dist/docs/ from b332a6b55668f60988e36961f3f62a794ba82ddb
|
||||
run: cd documentation && ./build_all_to_dist.sh
|
||||
|
||||
- name: Switch to current branch
|
||||
run: git checkout $echo "$(cat ~/current_branch)"
|
||||
- name: Build all projects in documentation/ & move to ~/dist/docs/ on current branch
|
||||
run: cd documentation && ./build_all_to_dist.sh && rm ~/current_branch
|
||||
|
||||
# End of replacemet
|
||||
|
||||
- name: Post process
|
||||
run: cd documentation && ./post_process.sh
|
||||
continue-on-error: false
|
||||
|
||||
- name: Create Vercel project file
|
||||
uses: mobiledevops/secret-to-file-action@v1
|
||||
|
||||
@@ -15,6 +15,24 @@ on:
|
||||
type: boolean
|
||||
schedule:
|
||||
- cron: "14 0 * * *"
|
||||
pull_request:
|
||||
paths:
|
||||
- "clients/**"
|
||||
- "common/**"
|
||||
- "explorer-api/**"
|
||||
- "gateway/**"
|
||||
- "integrations/**"
|
||||
- "mixnode/**"
|
||||
- "nym-api/**"
|
||||
- "nym-node/**"
|
||||
- "nym-outfox/**"
|
||||
- 'nym-data-observatory/**'
|
||||
- "nym-validator-rewarder/**"
|
||||
- "sdk/rust/nym-sdk/**"
|
||||
- "service-providers/**"
|
||||
- "tools/**"
|
||||
- "nymvisor/**"
|
||||
- ".github/workflows/ci-build-upload-binaries.yml"
|
||||
|
||||
jobs:
|
||||
publish-nym:
|
||||
|
||||
@@ -34,7 +34,6 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
|
||||
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 libudev-dev squashfs-tools protobuf-compiler
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
strategy:
|
||||
matrix:
|
||||
checks:
|
||||
|
||||
@@ -2,5 +2,9 @@
|
||||
{
|
||||
"rust":"stable",
|
||||
"runOnEvent":"always"
|
||||
},
|
||||
{
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
check-schema:
|
||||
name: Generate and check schema
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: arc-ubuntu-20.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
|
||||
@@ -2,6 +2,11 @@ name: ci-contracts-upload-binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'common/**'
|
||||
- 'contracts/**'
|
||||
- '.github/workflows/ci-contracts-upload-binaries.yml'
|
||||
|
||||
env:
|
||||
NETWORK: mainnet
|
||||
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: arc-ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
|
||||
@@ -3,35 +3,28 @@ name: ci-docs
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches-ignore: [master]
|
||||
branches-ignore: master
|
||||
paths:
|
||||
- "documentation/docs/**"
|
||||
- ".github/workflows/ci-docs.yml"
|
||||
- 'documentation/docs/**'
|
||||
- '.github/workflows/ci-docs.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arc-ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: documentation/docs
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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 git python3 && sudo apt-get update --fix-missing
|
||||
- name: Install pip3
|
||||
run: sudo apt install -y python3-pip
|
||||
run: sudo apt install -y python3-pip
|
||||
- name: Install Python3 modules
|
||||
run: sudo pip3 install pandas tabulate
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
with:
|
||||
version: 9
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -41,13 +34,30 @@ jobs:
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
- name: Install mdbook and plugins
|
||||
run: cd documentation && ./install_mdbook_deps.sh
|
||||
- name: Remove existing Nym config directory (`~/.nym/`)
|
||||
run: cd documentation && ./remove_existing_config.sh
|
||||
continue-on-error: false
|
||||
|
||||
- name: Install project dependencies
|
||||
run: pnpm i
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
- name: Move files to /dist/
|
||||
run: ../scripts/move-to-dist.sh
|
||||
# This is the original flow
|
||||
# - name: Build all projects in documentation/ & move to ~/dist/docs/
|
||||
# run: cd documentation && ./build_all_to_dist.sh
|
||||
|
||||
# This is a workaround replacement which builds on the last working commit b332a6b55668f60988e36961f3f62a794ba82ddb and then on current branch
|
||||
- name: Save current branch to ~/current_branch
|
||||
run: git rev-parse --abbrev-ref HEAD > ~/current_branch
|
||||
- name: Git pull, reset & switch to b332a6b55668f60988e36961f3f62a794ba82ddb
|
||||
run: git pull && git reset --hard && git checkout b332a6b55668f60988e36961f3f62a794ba82ddb
|
||||
- name: Build all projects in documentation/ & move to ~/dist/docs/ from b332a6b55668f60988e36961f3f62a794ba82ddb
|
||||
run: cd documentation && ./build_all_to_dist.sh
|
||||
|
||||
- name: Switch to current branch
|
||||
run: git checkout $echo "$(cat ~/current_branch)"
|
||||
- name: Build all projects in documentation/ & move to ~/dist/docs/ on current branch
|
||||
run: cd documentation && ./build_all_to_dist.sh && rm ~/current_branch
|
||||
|
||||
# End of replacemet
|
||||
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
@@ -58,5 +68,5 @@ jobs:
|
||||
SOURCE: "dist/docs/"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/docs-nextra-${{ env.GITHUB_REF_SLUG }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/docs-${{ env.GITHUB_REF_SLUG }}
|
||||
EXCLUDE: "/node_modules/"
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
name: ci-nym-api-tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "nym-api/**"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-api/tests
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: nym-api tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: install yarn in root
|
||||
run: cd ../.. && yarn install
|
||||
|
||||
- name: Install npm
|
||||
run: npm install
|
||||
|
||||
- name: Node v18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.1.0
|
||||
|
||||
- name: Install yarn
|
||||
run: yarn install
|
||||
|
||||
- name: Run yarn
|
||||
run: yarn
|
||||
|
||||
- name: Run tests
|
||||
run: yarn test:sandbox
|
||||
working-directory: nym-api/tests
|
||||
@@ -0,0 +1,45 @@
|
||||
name: ci-nym-credential-proxy
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'common/**'
|
||||
- 'nym-credential-proxy/**'
|
||||
- '.github/workspace/ci-nym-credential-proxy.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arc-ubuntu-22.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
MANIFEST_PATH: "--manifest-path nym-credential-proxy/Cargo.toml"
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: ${{ env.MANIFEST_PATH }} --all -- --check
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets -- -D warnings
|
||||
@@ -0,0 +1,79 @@
|
||||
name: ci-sdk-docs-typescript
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "sdk/typescript/**"
|
||||
- "wasm/**"
|
||||
- '.github/workflows/ci-sdk-docs-typescript.yml'
|
||||
|
||||
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.17
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
|
||||
- name: Build branch WASM packages
|
||||
run: make sdk-wasm-build
|
||||
|
||||
- name: Install
|
||||
run: yarn
|
||||
- name: Build
|
||||
run: yarn docs:prod:build
|
||||
- name: Deploy branch to CI www (docs)
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "dist/ts/"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/sdk-ts-docs-${{ 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: ts-packages
|
||||
NYM_PROJECT_NAME: "sdk-ts-docs"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}/docs/sdk/typescript"
|
||||
NYM_CI_WWW_LOCATION: "sdk-ts-docs-${{ 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
|
||||
@@ -4,9 +4,7 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
- "release/**"
|
||||
paths-ignore:
|
||||
- "documentation/**"
|
||||
- 'release/**'
|
||||
types:
|
||||
- labeled
|
||||
- unlabeled
|
||||
|
||||
@@ -55,7 +55,6 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -14,14 +14,13 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.77
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '114'
|
||||
run: cargo install --version 0.114.0 wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make contracts
|
||||
|
||||
@@ -56,6 +56,14 @@ jobs:
|
||||
rustup target add aarch64-linux-android \
|
||||
x86_64-linux-android
|
||||
|
||||
- name: Build lib nym-socks5-listener
|
||||
working-directory: sdk/lib/socks5-listener/
|
||||
env:
|
||||
RELEASE: true
|
||||
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
|
||||
# build for arm64 and x86_64
|
||||
run: ./build-android.sh aarch64 x86_64
|
||||
|
||||
- name: Build APKs (unsigned)
|
||||
working-directory: nym-connect/native/android
|
||||
env:
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.44.3
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
name: Build and upload Data observatory container to harbor.nymte.ch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-data-observatory"
|
||||
CONTAINER_NAME: "data-observatory"
|
||||
|
||||
jobs:
|
||||
build-container:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Login to Harbor
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: harbor.nymte.ch
|
||||
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
|
||||
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
git config --global user.email "lawrence@nymtech.net"
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
- name: Check if tag exists
|
||||
run: |
|
||||
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
|
||||
fi
|
||||
|
||||
- name: Remove existing tag if exists
|
||||
run: |
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
fi
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
|
||||
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
@@ -25,27 +25,31 @@ jobs:
|
||||
git config --global user.email "lawrence@nymtech.net"
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
- name: Get version from package.json
|
||||
uses: sergeysova/jq-action@v2
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
|
||||
|
||||
cmd: jq -r '.version' ${{ env.WORKING_DIRECTORY }}/package.json
|
||||
|
||||
- name: Check if tag exists
|
||||
run: |
|
||||
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
|
||||
fi
|
||||
|
||||
- name: Remove existing tag if exists
|
||||
run: |
|
||||
echo "Checking if tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} exists..."
|
||||
if git rev-parse ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
|
||||
echo "Tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} already exists"
|
||||
git push --delete origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
|
||||
git tag -d ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
|
||||
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ steps.get_version.outputs.value }}
|
||||
git tag -d ${{ steps.get_version.outputs.value }}
|
||||
fi
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git tag -a ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
|
||||
git push origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
|
||||
|
||||
git tag -a ${{ steps.get_version.outputs.value }} -m "Version ${{ steps.get_version.outputs.value }}"
|
||||
git push origin ${{ steps.get_version.outputs.value }}
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build -f nym-network-monitor.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
docker build -f nym-network-monitor.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.value }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
@@ -2,13 +2,9 @@ name: Build and upload Node Status agent container to harbor.nymte.ch
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gateway_probe_git_ref:
|
||||
type: string
|
||||
description: Which gateway probe git ref to build the image with
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent"
|
||||
WORKING_DIRECTORY: "nym-node-status-agent"
|
||||
CONTAINER_NAME: "node-status-agent"
|
||||
|
||||
jobs:
|
||||
@@ -31,31 +27,30 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.44.3
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
- name: cleanup-gateway-probe-ref
|
||||
id: cleanup_gateway_probe_ref
|
||||
- name: Check if tag exists
|
||||
run: |
|
||||
GATEWAY_PROBE_GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }}
|
||||
GIT_REF_SLUG="${GATEWAY_PROBE_GIT_REF//\//-}"
|
||||
echo "git_ref=${GIT_REF_SLUG}" >> $GITHUB_OUTPUT
|
||||
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
|
||||
fi
|
||||
|
||||
- name: Remove existing tag if exists
|
||||
run: |
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
|
||||
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
fi
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }} -m "Version ${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}"
|
||||
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
|
||||
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
|
||||
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
|
||||
@@ -3,7 +3,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
|
||||
WORKING_DIRECTORY: "nym-node-status-api"
|
||||
CONTAINER_NAME: "node-status-api"
|
||||
|
||||
jobs:
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.44.3
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
name: Build and upload nym node container to harbor.nymte.ch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-node"
|
||||
CONTAINER_NAME: "nym-node"
|
||||
|
||||
jobs:
|
||||
build-container:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Login to Harbor
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: harbor.nymte.ch
|
||||
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
|
||||
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
git config --global user.email "lawrence@nymtech.net"
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
- name: Check if tag exists
|
||||
run: |
|
||||
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
|
||||
fi
|
||||
|
||||
- name: Remove existing tag if exists
|
||||
run: |
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
fi
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
|
||||
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
@@ -1,55 +0,0 @@
|
||||
name: Build and upload Nyx Chain Watcher container to harbor.nymte.ch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nyx-chain-watcher"
|
||||
CONTAINER_NAME: "nyx-chain-watcher"
|
||||
|
||||
jobs:
|
||||
build-container:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Login to Harbor
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: harbor.nymte.ch
|
||||
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
|
||||
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
git config --global user.email "lawrence@nymtech.net"
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
- name: Check if tag exists
|
||||
run: |
|
||||
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
|
||||
fi
|
||||
|
||||
- name: Remove existing tag if exists
|
||||
run: |
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
fi
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
|
||||
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
@@ -1,49 +0,0 @@
|
||||
name: Build and upload Validator Rewarder container to harbor.nymte.ch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-validator-rewarder"
|
||||
CONTAINER_NAME: "validator-rewarder"
|
||||
|
||||
jobs:
|
||||
build-container:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Login to Harbor
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: harbor.nymte.ch
|
||||
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
|
||||
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
git config --global user.email "lawrence@nymtech.net"
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
- name: Remove existing tag if exists
|
||||
run: |
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
fi
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
|
||||
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
@@ -1,49 +0,0 @@
|
||||
# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.
|
||||
# See https://redocly.com/docs/cli/ for more information.
|
||||
formatted-openapi.json:
|
||||
path-parameters-defined:
|
||||
# - >-
|
||||
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/0/name
|
||||
# - >-
|
||||
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/1/name
|
||||
# - >-
|
||||
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/2/name
|
||||
# - >-
|
||||
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/3/name
|
||||
# - >-
|
||||
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/4/name
|
||||
# - >-
|
||||
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/5/name
|
||||
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/0/name'
|
||||
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/1/name'
|
||||
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/2/name'
|
||||
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/3/name'
|
||||
operation-operationId-unique:
|
||||
- >-
|
||||
#/paths/~1v1~1status~1mixnodes~1active~1detailed/get/get_active_set_detailed
|
||||
- '#/paths/~1v1~1status~1mixnodes~1detailed/get/get_mixnodes_detailed'
|
||||
- >-
|
||||
#/paths/~1v1~1status~1mixnodes~1rewarded~1detailed/get/get_rewarded_set_detailed
|
||||
no-unused-components:
|
||||
- '#/components/schemas/AxumErrorResponse'
|
||||
- '#/components/schemas/DateQuery'
|
||||
- '#/components/schemas/EcashTicketVerificationRejection'
|
||||
- '#/components/schemas/ExpirationDatePathParam'
|
||||
- '#/components/schemas/FullFatNode'
|
||||
- '#/components/schemas/HistoricalPerformanceResponse'
|
||||
- '#/components/schemas/HistoricalUptimeResponse'
|
||||
- '#/components/schemas/MasterVerificationKeyResponse'
|
||||
- '#/components/schemas/MixnodeStatusReport'
|
||||
- '#/components/schemas/NodeId'
|
||||
- '#/components/schemas/NodeRoleQueryParam'
|
||||
- '#/components/schemas/NoiseDetails'
|
||||
- '#/components/schemas/NymNodeDescription'
|
||||
- '#/components/schemas/NymNodeDetails'
|
||||
- '#/components/schemas/PaginationRequest'
|
||||
- '#/components/schemas/PartialCoinIndicesSignatureResponse'
|
||||
- '#/components/schemas/SpentCredentialsResponse'
|
||||
- '#/components/schemas/UptimeHistoryResponse'
|
||||
- '#/components/schemas/VerifyEcashCredentialBody'
|
||||
- '#/components/responses/AxumErrorResponse'
|
||||
- '#/components/responses/CirculatingSupplyResponse'
|
||||
- '#/components/responses/RequestError'
|
||||
@@ -1,83 +0,0 @@
|
||||
extends:
|
||||
- minimal
|
||||
apis:
|
||||
nym-api:
|
||||
root: ./formatted-openapi.json
|
||||
rules:
|
||||
# https://redocly.com/docs/cli/rules/oas/operation-summary
|
||||
operation-summary: off
|
||||
# https://redocly.com/docs/cli/rules/oas/security-defined
|
||||
security-defined: off
|
||||
struct: off
|
||||
# https://redocly.com/docs/cli/rules/oas/operation-2xx-response
|
||||
operation-2xx-response: off
|
||||
# rules:
|
||||
# skip-warnings: true
|
||||
# ignore:
|
||||
# - path: /v1/gateways
|
||||
# method: get
|
||||
# - path: /v1/gateways/blacklisted
|
||||
# method: get
|
||||
# - path: /v1/mixnodes
|
||||
# method: get
|
||||
# - path: /v1/mixnodes/active
|
||||
# method: get
|
||||
# - path: /v1/mixnodes/active/detailed
|
||||
# method: get
|
||||
# - path: /v1/mixnodes/blacklisted
|
||||
# method: get
|
||||
# - path: /v1/mixnodes/detailed
|
||||
# method: get
|
||||
# - path: /v1/mixnodes/rewarded
|
||||
# method: get
|
||||
# - path: /v1/mixnodes/rewarded/detailed
|
||||
# method: get
|
||||
# - path: /v1/gateways/described
|
||||
# method: get
|
||||
# # network-monitor-status (deprecated)
|
||||
# - path: /v1/status/gateway/{identity}/avg_uptime
|
||||
# method: GET
|
||||
# - path: /v1/status/gateway/{identity}/core-status-count
|
||||
# method: GET
|
||||
# - path: /v1/status/gateway/{identity}/history
|
||||
# method: GET
|
||||
# - path: /v1/status/gateway/{identity}/report
|
||||
# method: GET
|
||||
# - path: /v1/status/gateways/detailed
|
||||
# method: GET
|
||||
# - path: /v1/status/gateways/detailed-unfiltered
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnode/{mix_id}/avg_uptime
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnode/{mix_id}/compute-reward-estimation
|
||||
# method: POST
|
||||
# - path: /v1/status/mixnode/{mix_id}/core-status-count
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnode/{mix_id}/history
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnode/{mix_id}/report
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnode/{mix_id}/reward-estimation
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnodes/detailed-unfiltered
|
||||
# method: GET
|
||||
# # status
|
||||
# - path: /v1/status/mixnode/{mix_id}/inclusion-probability
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnode/{mix_id}/stake-saturation
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnode/{mix_id}/status
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnodes/active/detailed
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnodes/detailed
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnodes/inclusion-probability
|
||||
# method: GET
|
||||
# - path: /v1/status/mixnodes/rewarded/detailed
|
||||
# method: GET
|
||||
# # unstable nym nodes
|
||||
# - path: /v1/unstable/nym-nodes/gateways/skimmed
|
||||
# method: get
|
||||
# - path: /v1/unstable/nym-nodes/mixnodes/skimmed
|
||||
# method: get
|
||||
-344
@@ -4,350 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.14-crunch] (2024-12-11)
|
||||
|
||||
- Merge/release/2024.14-crunch ([#5242])
|
||||
- bugfix: added explicit openapi servers to account for route prefixes ([#5237])
|
||||
- Further config score adjustments ([#5225])
|
||||
- feature: remve any filtering on node semver ([#5224])
|
||||
- Backport #5218 ([#5220])
|
||||
- Derive serialize for UserAgent (#5210) ([#5217])
|
||||
- dont consider legacy nodes for rewarded set selection ([#5215])
|
||||
- introduce UNSTABLE endpoints for returning network monitor run details ([#5214])
|
||||
- Nmv2 add debug config ([#5212])
|
||||
- nym-api NMv1 adjustments ([#5209])
|
||||
- adjusted config score penalty calculation ([#5206])
|
||||
- Fix backwards compat mac generation ([#5202])
|
||||
- merge crunch into develop ([#5199])
|
||||
- Update Security disclosure email, public key and policy ([#5195])
|
||||
- Guard storage access with cache ([#5193])
|
||||
- chore: apply 1.84 linter suggestions ([#5192])
|
||||
- improvement: make internal gateway clients use the same topology cache ([#5191])
|
||||
- Bugfix/credential proxy sequencing ([#5187])
|
||||
- Add monitor_run and testing_route indexes ([#5182])
|
||||
- Add indexes to monitor run and testing route ([#5181])
|
||||
- bugfix: fixed nym-node config migrations (again) ([#5179])
|
||||
- bugfix: use default value for verloc config when deserialising missing values ([#5177])
|
||||
- Remove peers with no allowed ip from storage ([#5175])
|
||||
- Move two minor jobs to free tier github hosted runners ([#5169])
|
||||
- Add support for DELETE to nym-http-api-client ([#5166])
|
||||
- Fix env var name ([#5165])
|
||||
- Add strum::EnumIter for TicketType ([#5164])
|
||||
- Add export_to_env to NymNetworkDetails ([#5162])
|
||||
- bugfix: correctly expose ecash-related data on nym-api ([#5155])
|
||||
- fix: validator-rewarder GH job ([#5151])
|
||||
- build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet ([#5150])
|
||||
- build(deps): bump mikefarah/yq from 4.44.3 to 4.44.5 ([#5149])
|
||||
- start session collection for exit gateways ([#5148])
|
||||
- add version to clientStatsReport ([#5147])
|
||||
- update serde_json_path due to compilation issue ([#5144])
|
||||
- chore: remove standalone legacy mixnode/gateway binaries ([#5135])
|
||||
- [Product Data] Set up country reporting from vpn-client ([#5134])
|
||||
- removed ci-nym-api-tests.yml which was running outdated (and broken) tests ([#5133])
|
||||
- CI: reduce jobs running on cluster ([#5132])
|
||||
- [DOCS/operators]: Release changes v2024.13-magura & Tokenomics pages v1.0 ([#5128])
|
||||
- NS Agent auth with NS API ([#5127])
|
||||
- [Product Data] Config deserialization bug fix ([#5126])
|
||||
- bugfix: don't send empty BankMsg in ecash contract ([#5121])
|
||||
- [Product data] Data consumption with ecash ticket ([#5120])
|
||||
- feat: add GH workflow for nym-validator-rewarder ([#5119])
|
||||
- feat: add Dockerfile and add env vars for clap arguments ([#5118])
|
||||
- feature: config score ([#5117])
|
||||
- [Product Data] Add stats reporting configuration in client config ([#5115])
|
||||
- Correct IPv6 address generation ([#5113])
|
||||
- feature: rewarding for ticketbook issuance ([#5112])
|
||||
- Add granular log on nym-node ([#5111])
|
||||
- Send mixnet packet stats using task client ([#5109])
|
||||
- Expose time range ([#5108])
|
||||
- [Product Data] Client-side stats collection ([#5107])
|
||||
- chore: ecash contract migration to remove unused 'redemption_gateway_share' ([#5104])
|
||||
- [Product Data] Better unique user count on gateways ([#5084])
|
||||
- feat: add nym node GH workflow ([#5080])
|
||||
- IPv6 support for wireguard ([#5059])
|
||||
- Node Status API ([#5050])
|
||||
- Authenticator CLI client mode ([#5044])
|
||||
- Integrate nym-credential-proxy into workspace ([#5027])
|
||||
- [Product Data] Introduce data persistence on gateways ([#5022])
|
||||
- Bump the patch-updates group across 1 directory with 10 updates ([#5011])
|
||||
- build(deps): bump once_cell from 1.19.0 to 1.20.2 ([#4952])
|
||||
- Create TaskStatusEvent trait instead of piggybacking on Error ([#4919])
|
||||
- build(deps): bump lazy_static from 1.4.0 to 1.5.0 ([#4913])
|
||||
- Sync code with .env in build.rs ([#4876])
|
||||
- build(deps): bump axios from 1.6.0 to 1.7.5 in /nym-api/tests ([#4790])
|
||||
- Bump elliptic from 6.5.4 to 6.5.7 in /testnet-faucet ([#4768])
|
||||
|
||||
[#5242]: https://github.com/nymtech/nym/pull/5242
|
||||
[#5237]: https://github.com/nymtech/nym/pull/5237
|
||||
[#5225]: https://github.com/nymtech/nym/pull/5225
|
||||
[#5224]: https://github.com/nymtech/nym/pull/5224
|
||||
[#5220]: https://github.com/nymtech/nym/pull/5220
|
||||
[#5217]: https://github.com/nymtech/nym/pull/5217
|
||||
[#5215]: https://github.com/nymtech/nym/pull/5215
|
||||
[#5214]: https://github.com/nymtech/nym/pull/5214
|
||||
[#5212]: https://github.com/nymtech/nym/pull/5212
|
||||
[#5209]: https://github.com/nymtech/nym/pull/5209
|
||||
[#5206]: https://github.com/nymtech/nym/pull/5206
|
||||
[#5202]: https://github.com/nymtech/nym/pull/5202
|
||||
[#5199]: https://github.com/nymtech/nym/pull/5199
|
||||
[#5195]: https://github.com/nymtech/nym/pull/5195
|
||||
[#5193]: https://github.com/nymtech/nym/pull/5193
|
||||
[#5192]: https://github.com/nymtech/nym/pull/5192
|
||||
[#5191]: https://github.com/nymtech/nym/pull/5191
|
||||
[#5187]: https://github.com/nymtech/nym/pull/5187
|
||||
[#5182]: https://github.com/nymtech/nym/pull/5182
|
||||
[#5181]: https://github.com/nymtech/nym/pull/5181
|
||||
[#5179]: https://github.com/nymtech/nym/pull/5179
|
||||
[#5177]: https://github.com/nymtech/nym/pull/5177
|
||||
[#5175]: https://github.com/nymtech/nym/pull/5175
|
||||
[#5169]: https://github.com/nymtech/nym/pull/5169
|
||||
[#5166]: https://github.com/nymtech/nym/pull/5166
|
||||
[#5165]: https://github.com/nymtech/nym/pull/5165
|
||||
[#5164]: https://github.com/nymtech/nym/pull/5164
|
||||
[#5162]: https://github.com/nymtech/nym/pull/5162
|
||||
[#5155]: https://github.com/nymtech/nym/pull/5155
|
||||
[#5151]: https://github.com/nymtech/nym/pull/5151
|
||||
[#5150]: https://github.com/nymtech/nym/pull/5150
|
||||
[#5149]: https://github.com/nymtech/nym/pull/5149
|
||||
[#5148]: https://github.com/nymtech/nym/pull/5148
|
||||
[#5147]: https://github.com/nymtech/nym/pull/5147
|
||||
[#5144]: https://github.com/nymtech/nym/pull/5144
|
||||
[#5135]: https://github.com/nymtech/nym/pull/5135
|
||||
[#5134]: https://github.com/nymtech/nym/pull/5134
|
||||
[#5133]: https://github.com/nymtech/nym/pull/5133
|
||||
[#5132]: https://github.com/nymtech/nym/pull/5132
|
||||
[#5128]: https://github.com/nymtech/nym/pull/5128
|
||||
[#5127]: https://github.com/nymtech/nym/pull/5127
|
||||
[#5126]: https://github.com/nymtech/nym/pull/5126
|
||||
[#5121]: https://github.com/nymtech/nym/pull/5121
|
||||
[#5120]: https://github.com/nymtech/nym/pull/5120
|
||||
[#5119]: https://github.com/nymtech/nym/pull/5119
|
||||
[#5118]: https://github.com/nymtech/nym/pull/5118
|
||||
[#5117]: https://github.com/nymtech/nym/pull/5117
|
||||
[#5115]: https://github.com/nymtech/nym/pull/5115
|
||||
[#5113]: https://github.com/nymtech/nym/pull/5113
|
||||
[#5112]: https://github.com/nymtech/nym/pull/5112
|
||||
[#5111]: https://github.com/nymtech/nym/pull/5111
|
||||
[#5109]: https://github.com/nymtech/nym/pull/5109
|
||||
[#5108]: https://github.com/nymtech/nym/pull/5108
|
||||
[#5107]: https://github.com/nymtech/nym/pull/5107
|
||||
[#5104]: https://github.com/nymtech/nym/pull/5104
|
||||
[#5084]: https://github.com/nymtech/nym/pull/5084
|
||||
[#5080]: https://github.com/nymtech/nym/pull/5080
|
||||
[#5059]: https://github.com/nymtech/nym/pull/5059
|
||||
[#5050]: https://github.com/nymtech/nym/pull/5050
|
||||
[#5044]: https://github.com/nymtech/nym/pull/5044
|
||||
[#5027]: https://github.com/nymtech/nym/pull/5027
|
||||
[#5022]: https://github.com/nymtech/nym/pull/5022
|
||||
[#5011]: https://github.com/nymtech/nym/pull/5011
|
||||
[#4952]: https://github.com/nymtech/nym/pull/4952
|
||||
[#4919]: https://github.com/nymtech/nym/pull/4919
|
||||
[#4913]: https://github.com/nymtech/nym/pull/4913
|
||||
[#4876]: https://github.com/nymtech/nym/pull/4876
|
||||
[#4790]: https://github.com/nymtech/nym/pull/4790
|
||||
[#4768]: https://github.com/nymtech/nym/pull/4768
|
||||
|
||||
## [2024.13-magura-drift] (2024-11-29)
|
||||
|
||||
- Optimised syncing bandwidth information to storage
|
||||
|
||||
## [2024.13-magura-patched] (2024-11-22)
|
||||
|
||||
- [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution
|
||||
- Introduced a configurable limit on retransmission frequency of packets if ACKs are not received
|
||||
- Filtered out invalid IP addresses on nym-api
|
||||
|
||||
## [2024.13-magura] (2024-11-18)
|
||||
|
||||
- Limit race probability ([#5145])
|
||||
- bugifx: assign 'node_id' when converting from 'GatewayDetails' to 'TestNode' ([#5143])
|
||||
- bugfix: make sure to assign correct node_id and identity during 'gateway_details' table migration ([#5142])
|
||||
- Respond to auth messages with same version ([#5140])
|
||||
- Pain/polyfill deprecated endpoints ([#5131])
|
||||
- change: dont select mixnodes bonded with vested tokens into the rewarded set ([#5129])
|
||||
- nym-credential-proxy-requests: reqwest use rustls-tls ([#5116])
|
||||
- bugfix: preserve as much as possible of the rewarded set during migration ([#5103])
|
||||
- Feature/force refresh node ([#5101])
|
||||
- Add NYM_VPN_API to env files ([#5099])
|
||||
- bugfix: fixed historical uptimes for nodes ([#5097])
|
||||
- Remove old use of 1GB constant ([#5096])
|
||||
- Graceful agent 1.1.5 ([#5093])
|
||||
- Add more translations from v2 to v3 authenticator ([#5091])
|
||||
- Nym node - Fix claim delegator rewards ([#5090])
|
||||
- Make 250 GB/30 days for free ride mode ([#5083])
|
||||
- Don't increase bandwidth two times ([#5081])
|
||||
- Fix expiration date as today + 7 days ([#5076])
|
||||
- Fix gateway decreasing bandwidth ([#5075])
|
||||
- Allow custom http port to be reset ([#5073])
|
||||
- bugfix: additional checks inside credential proxy ([#5072])
|
||||
- chore: deprecated old nym-api client methods and replaced them when possible ([#5069])
|
||||
- NS API with directory v2 (#5058) ([#5068])
|
||||
- bugfix: credential-proxy obtain-async ([#5067])
|
||||
- Allow nym node config updates ([#5066])
|
||||
- bugfix: use corrext axum extractors for ecash route arguments ([#5065])
|
||||
- Merge2/release/2024.13 magura ([#5063])
|
||||
- bugfix/feature: added NymApiClient method to get all skimmed nodes ([#5062])
|
||||
- Merge1/release/2024.13 magura ([#5061])
|
||||
- added hacky routes to return nymnodes alongside legacy nodes ([#5051])
|
||||
- bugfix: mark migrated gateways as rewarded in the previous epoch in case theyre in the rewarded set ([#5049])
|
||||
- bugfix: adjust runtime storage migration ([#5047])
|
||||
- bugfix: supersede 'cb13be27f8f61d9ae74d924e85d2e6787895eb14' by using… ([#5046])
|
||||
- bugfix: restore default http port for nym-api ([#5045])
|
||||
- bugfix: fix ecash handlers routes ([#5043])
|
||||
- bugfix: don't assign exit gateways to standby set ([#5041])
|
||||
- bugfix: make sure nym-nodes are also tested by network monitor ([#5040])
|
||||
- bugfix: use bonded nym-nodes for determining initial network monitor … ([#5039])
|
||||
- bugfix: make gateways insert themselves into [local] topology ([#5038])
|
||||
- Pass poisson flag ([#5037])
|
||||
- bugfix: use human readable roles for annotations ([#5036])
|
||||
- bugfix: use old name for 'epoch_role' in SkimmedNode ([#5034])
|
||||
- bugfix: make sure to use correct highest node id when assigning role ([#5032])
|
||||
- feature: use axum_client_ip for attempting to extract source ip ([#5031])
|
||||
- bugfix: fixed backwards incompatibility for /gateways/described endpoint ([#5030])
|
||||
- bugfix: verifying signed information of legacy nodes ([#5029])
|
||||
- bugfix: introduce 'LegacyPendingMixNodeChanges' that does not contain 'cost_params_change' ([#5028])
|
||||
- bugfix: missing #[serde(default)] for announce port ([#5024])
|
||||
- bugfix: directory v2.1 `get_all_avg_gateway_reliability_in_interval` query ([#5023])
|
||||
- added 'get_all_described_nodes' to NymApiClient and adjusted return t… ([#5016])
|
||||
- Reapply fixes to new branch ([#5014])
|
||||
- Consume only positive bandwidth ([#5013])
|
||||
- feature: adjusted ticket sizes to the agreed amounts ([#5009])
|
||||
- Push private ip before inserting ([#5008])
|
||||
- chore: update itertools in compact ecash ([#4994])
|
||||
- feature: make accepting t&c a hard requirement for rewarded set selection ([#4993])
|
||||
- Fix rustfmt in nym-credential-proxy ([#4992])
|
||||
- bugfix: client memory leak ([#4991])
|
||||
- Eliminate 0 bandwidth race check ([#4988])
|
||||
- [DOCs;/operators]: Release notes for v2024.12 aero ([#4984])
|
||||
- Add topup req constructor ([#4983])
|
||||
- Fix critical issues SI86 and SI87 from Cure53 ([#4982])
|
||||
- Rename nym-vpn-api to nym-credential-proxy ([#4981])
|
||||
- enable global ecash routes even if api is not a signer ([#4980])
|
||||
- resolve beta clippy issues in contracts ([#4978])
|
||||
- Re-enable vested delegation migration ([#4977])
|
||||
- feature: require reporting using nym-node binary for rewarded set selection ([#4976])
|
||||
- Top up bandwidth ([#4975])
|
||||
- [Product Data] Add session type based on ecash ticket received ([#4974])
|
||||
- Bugfix/additional directory fixes ([#4973])
|
||||
- feat: add Dockerfile for nym node ([#4972])
|
||||
- chore: remove unused rocket code ([#4968])
|
||||
- Import nym-vpn-api crates ([#4967])
|
||||
- feature: importer-cli to correctly handle mixnet/vesting import ([#4966])
|
||||
- bugfix: fix expected return type on /v1/gateways endpoint ([#4965])
|
||||
- [Product Data] First step in gateway usage data collection ([#4963])
|
||||
- Bump sqlx to 0.7.4 ([#4959])
|
||||
- Add env feature to clap and make clap parameters available as env variables ([#4957])
|
||||
- Feature/contract state tools ([#4954])
|
||||
- expose authenticator address along other address in node-details ([#4953])
|
||||
- Extract packet processing from mixnode-common ([#4949])
|
||||
- nym-api container ([#4948])
|
||||
- Ticket type storage ([#4947])
|
||||
- Add "utoipa" feature to nym-node ([#4945])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 9 updates ([#4944])
|
||||
- V2 performance monitoring feature flag ([#4943])
|
||||
- Bugfix/rewarder post pruning adjustments ([#4942])
|
||||
- Switch over the last set of jobs to arc runners ([#4938])
|
||||
- Fix broken build after merge ([#4937])
|
||||
- bugfix: correctly paginate through 'search_tx' endpoint ([#4936])
|
||||
- Add more conversions for responses of authenticator messages ([#4929])
|
||||
- Directory Sevices v2.1 ([#4903])
|
||||
- Migrate Legacy Node (Frontend) ([#4826])
|
||||
- Fix critical issues SI84 and SI85 from Cure53 ([#4758])
|
||||
|
||||
[#5145]: https://github.com/nymtech/nym/pull/5145
|
||||
[#5143]: https://github.com/nymtech/nym/pull/5143
|
||||
[#5142]: https://github.com/nymtech/nym/pull/5142
|
||||
[#5140]: https://github.com/nymtech/nym/pull/5140
|
||||
[#5131]: https://github.com/nymtech/nym/pull/5131
|
||||
[#5129]: https://github.com/nymtech/nym/pull/5129
|
||||
[#5116]: https://github.com/nymtech/nym/pull/5116
|
||||
[#5103]: https://github.com/nymtech/nym/pull/5103
|
||||
[#5101]: https://github.com/nymtech/nym/pull/5101
|
||||
[#5099]: https://github.com/nymtech/nym/pull/5099
|
||||
[#5097]: https://github.com/nymtech/nym/pull/5097
|
||||
[#5096]: https://github.com/nymtech/nym/pull/5096
|
||||
[#5093]: https://github.com/nymtech/nym/pull/5093
|
||||
[#5091]: https://github.com/nymtech/nym/pull/5091
|
||||
[#5090]: https://github.com/nymtech/nym/pull/5090
|
||||
[#5083]: https://github.com/nymtech/nym/pull/5083
|
||||
[#5081]: https://github.com/nymtech/nym/pull/5081
|
||||
[#5076]: https://github.com/nymtech/nym/pull/5076
|
||||
[#5075]: https://github.com/nymtech/nym/pull/5075
|
||||
[#5073]: https://github.com/nymtech/nym/pull/5073
|
||||
[#5072]: https://github.com/nymtech/nym/pull/5072
|
||||
[#5069]: https://github.com/nymtech/nym/pull/5069
|
||||
[#5068]: https://github.com/nymtech/nym/pull/5068
|
||||
[#5067]: https://github.com/nymtech/nym/pull/5067
|
||||
[#5066]: https://github.com/nymtech/nym/pull/5066
|
||||
[#5065]: https://github.com/nymtech/nym/pull/5065
|
||||
[#5063]: https://github.com/nymtech/nym/pull/5063
|
||||
[#5062]: https://github.com/nymtech/nym/pull/5062
|
||||
[#5061]: https://github.com/nymtech/nym/pull/5061
|
||||
[#5051]: https://github.com/nymtech/nym/pull/5051
|
||||
[#5049]: https://github.com/nymtech/nym/pull/5049
|
||||
[#5047]: https://github.com/nymtech/nym/pull/5047
|
||||
[#5046]: https://github.com/nymtech/nym/pull/5046
|
||||
[#5045]: https://github.com/nymtech/nym/pull/5045
|
||||
[#5043]: https://github.com/nymtech/nym/pull/5043
|
||||
[#5041]: https://github.com/nymtech/nym/pull/5041
|
||||
[#5040]: https://github.com/nymtech/nym/pull/5040
|
||||
[#5039]: https://github.com/nymtech/nym/pull/5039
|
||||
[#5038]: https://github.com/nymtech/nym/pull/5038
|
||||
[#5037]: https://github.com/nymtech/nym/pull/5037
|
||||
[#5036]: https://github.com/nymtech/nym/pull/5036
|
||||
[#5034]: https://github.com/nymtech/nym/pull/5034
|
||||
[#5032]: https://github.com/nymtech/nym/pull/5032
|
||||
[#5031]: https://github.com/nymtech/nym/pull/5031
|
||||
[#5030]: https://github.com/nymtech/nym/pull/5030
|
||||
[#5029]: https://github.com/nymtech/nym/pull/5029
|
||||
[#5028]: https://github.com/nymtech/nym/pull/5028
|
||||
[#5024]: https://github.com/nymtech/nym/pull/5024
|
||||
[#5023]: https://github.com/nymtech/nym/pull/5023
|
||||
[#5016]: https://github.com/nymtech/nym/pull/5016
|
||||
[#5014]: https://github.com/nymtech/nym/pull/5014
|
||||
[#5013]: https://github.com/nymtech/nym/pull/5013
|
||||
[#5009]: https://github.com/nymtech/nym/pull/5009
|
||||
[#5008]: https://github.com/nymtech/nym/pull/5008
|
||||
[#4994]: https://github.com/nymtech/nym/pull/4994
|
||||
[#4993]: https://github.com/nymtech/nym/pull/4993
|
||||
[#4992]: https://github.com/nymtech/nym/pull/4992
|
||||
[#4991]: https://github.com/nymtech/nym/pull/4991
|
||||
[#4988]: https://github.com/nymtech/nym/pull/4988
|
||||
[#4984]: https://github.com/nymtech/nym/pull/4984
|
||||
[#4983]: https://github.com/nymtech/nym/pull/4983
|
||||
[#4982]: https://github.com/nymtech/nym/pull/4982
|
||||
[#4981]: https://github.com/nymtech/nym/pull/4981
|
||||
[#4980]: https://github.com/nymtech/nym/pull/4980
|
||||
[#4978]: https://github.com/nymtech/nym/pull/4978
|
||||
[#4977]: https://github.com/nymtech/nym/pull/4977
|
||||
[#4976]: https://github.com/nymtech/nym/pull/4976
|
||||
[#4975]: https://github.com/nymtech/nym/pull/4975
|
||||
[#4974]: https://github.com/nymtech/nym/pull/4974
|
||||
[#4973]: https://github.com/nymtech/nym/pull/4973
|
||||
[#4972]: https://github.com/nymtech/nym/pull/4972
|
||||
[#4968]: https://github.com/nymtech/nym/pull/4968
|
||||
[#4967]: https://github.com/nymtech/nym/pull/4967
|
||||
[#4966]: https://github.com/nymtech/nym/pull/4966
|
||||
[#4965]: https://github.com/nymtech/nym/pull/4965
|
||||
[#4963]: https://github.com/nymtech/nym/pull/4963
|
||||
[#4959]: https://github.com/nymtech/nym/pull/4959
|
||||
[#4957]: https://github.com/nymtech/nym/pull/4957
|
||||
[#4954]: https://github.com/nymtech/nym/pull/4954
|
||||
[#4953]: https://github.com/nymtech/nym/pull/4953
|
||||
[#4949]: https://github.com/nymtech/nym/pull/4949
|
||||
[#4948]: https://github.com/nymtech/nym/pull/4948
|
||||
[#4947]: https://github.com/nymtech/nym/pull/4947
|
||||
[#4945]: https://github.com/nymtech/nym/pull/4945
|
||||
[#4944]: https://github.com/nymtech/nym/pull/4944
|
||||
[#4943]: https://github.com/nymtech/nym/pull/4943
|
||||
[#4942]: https://github.com/nymtech/nym/pull/4942
|
||||
[#4938]: https://github.com/nymtech/nym/pull/4938
|
||||
[#4937]: https://github.com/nymtech/nym/pull/4937
|
||||
[#4936]: https://github.com/nymtech/nym/pull/4936
|
||||
[#4929]: https://github.com/nymtech/nym/pull/4929
|
||||
[#4903]: https://github.com/nymtech/nym/pull/4903
|
||||
[#4826]: https://github.com/nymtech/nym/pull/4826
|
||||
[#4758]: https://github.com/nymtech/nym/pull/4758
|
||||
|
||||
## [2024.12-aero] (2024-10-17)
|
||||
|
||||
- nym-node: don't use bloomfilters for double spending checks ([#4960])
|
||||
|
||||
Generated
+616
-514
File diff suppressed because it is too large
Load Diff
+50
-46
@@ -19,33 +19,33 @@ members = [
|
||||
"clients/native",
|
||||
"clients/native/websocket-requests",
|
||||
"clients/socks5",
|
||||
"common/async-file-watcher",
|
||||
"common/authenticator-requests",
|
||||
"common/async-file-watcher",
|
||||
"common/bandwidth-controller",
|
||||
"common/bin-common",
|
||||
"common/client-core",
|
||||
"common/client-core/config-types",
|
||||
"common/client-core/gateways-storage",
|
||||
"common/client-core/surb-storage",
|
||||
"common/client-core/gateways-storage",
|
||||
"common/client-libs/gateway-client",
|
||||
"common/client-libs/mixnet-client",
|
||||
"common/client-libs/validator-client",
|
||||
"common/commands",
|
||||
"common/config",
|
||||
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
|
||||
"common/cosmwasm-smart-contracts/ecash-contract",
|
||||
"common/cosmwasm-smart-contracts/coconut-dkg",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
"common/cosmwasm-smart-contracts/ecash-contract",
|
||||
"common/cosmwasm-smart-contracts/group-contract",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/multisig-contract",
|
||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||
"common/country-group",
|
||||
"common/credential-storage",
|
||||
"common/credential-utils",
|
||||
"common/credential-verification",
|
||||
"common/credentials",
|
||||
"common/credential-utils",
|
||||
"common/credentials-interface",
|
||||
"common/credential-verification",
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/ecash-double-spending",
|
||||
@@ -54,20 +54,20 @@ members = [
|
||||
"common/exit-policy",
|
||||
"common/gateway-requests",
|
||||
"common/gateway-storage",
|
||||
"common/gateway-stats-storage",
|
||||
"common/http-api-client",
|
||||
"common/http-api-common",
|
||||
"common/inclusion-probability",
|
||||
"common/ip-packet-requests",
|
||||
"common/ledger",
|
||||
"common/mixnode-common",
|
||||
"common/models",
|
||||
"common/network-defaults",
|
||||
"common/node-tester-utils",
|
||||
"common/nonexhaustive-delayqueue",
|
||||
"common/nymcoconut",
|
||||
"common/nym_offline_compact_ecash",
|
||||
"common/nym-id",
|
||||
"common/nym-metrics",
|
||||
"common/nym_offline_compact_ecash",
|
||||
"common/nymcoconut",
|
||||
"common/nymsphinx",
|
||||
"common/nymsphinx/acknowledgements",
|
||||
"common/nymsphinx/addressing",
|
||||
@@ -89,7 +89,6 @@ members = [
|
||||
"common/statistics",
|
||||
"common/store-cipher",
|
||||
"common/task",
|
||||
"common/ticketbooks-merkle",
|
||||
"common/topology",
|
||||
"common/tun",
|
||||
"common/types",
|
||||
@@ -98,34 +97,31 @@ members = [
|
||||
"common/wasm/utils",
|
||||
"common/wireguard",
|
||||
"common/wireguard-types",
|
||||
"documentation/autodoc",
|
||||
"explorer-api",
|
||||
"explorer-api/explorer-api-requests",
|
||||
"explorer-api/explorer-client",
|
||||
"gateway",
|
||||
"integrations/bity",
|
||||
"sdk/ffi/cpp",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/shared",
|
||||
"mixnode",
|
||||
"sdk/lib/socks5-listener",
|
||||
"sdk/rust/nym-sdk",
|
||||
"sdk/ffi/shared",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/cpp",
|
||||
"service-providers/authenticator",
|
||||
"service-providers/common",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-credential-proxy/nym-credential-proxy-requests",
|
||||
"nym-credential-proxy/vpn-api-lib-wasm",
|
||||
"nym-data-observatory",
|
||||
"nym-network-monitor",
|
||||
"nym-api",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-data-observatory",
|
||||
"nym-node",
|
||||
"nym-node/nym-node-http-api",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-node/nym-node-metrics",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-node-status-api/nym-node-status-api",
|
||||
"nym-node-status-api/nym-node-status-client",
|
||||
"nym-node-status-api",
|
||||
"nym-node-status-agent",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"tools/echo-server",
|
||||
@@ -143,24 +139,26 @@ members = [
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract", "common/verloc", "tools/internal/mixnet-connectivity-check",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
"clients/native",
|
||||
"clients/socks5",
|
||||
"common/models",
|
||||
"explorer-api",
|
||||
"gateway",
|
||||
"mixnode",
|
||||
"nym-api",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-data-observatory",
|
||||
"nym-node",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-node-status-api/nym-node-status-api",
|
||||
"nym-node-status-api",
|
||||
"nym-validator-rewarder",
|
||||
"nym-node-status-api",
|
||||
"service-providers/authenticator",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
@@ -191,7 +189,7 @@ aes = "0.8.1"
|
||||
aes-gcm = "0.10.1"
|
||||
aes-gcm-siv = "0.11.1"
|
||||
aead = "0.5.2"
|
||||
anyhow = "1.0.90"
|
||||
anyhow = "1.0.89"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.83"
|
||||
axum-client-ip = "0.6.1"
|
||||
@@ -200,8 +198,11 @@ axum-extra = "0.9.4"
|
||||
base64 = "0.22.1"
|
||||
bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
bit-vec = "0.7.0" # can we unify those?
|
||||
|
||||
# can we unify those?
|
||||
bit-vec = "0.7.0"
|
||||
bitvec = "1.0.0"
|
||||
|
||||
blake3 = "1.5.4"
|
||||
bloomfilter = "1.0.14"
|
||||
bs58 = "0.5.1"
|
||||
@@ -214,7 +215,7 @@ chacha20 = "0.9.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.31"
|
||||
cipher = "0.4.3"
|
||||
clap = "4.5.20"
|
||||
clap = "4.5.18"
|
||||
clap_complete = "4.5"
|
||||
clap_complete_fig = "4.5"
|
||||
colored = "2.0"
|
||||
@@ -259,7 +260,6 @@ http-body-util = "0.1"
|
||||
httpcodec = "0.2.3"
|
||||
humantime = "2.1.0"
|
||||
humantime-serde = "1.1.1"
|
||||
human-repr = "1.1.0"
|
||||
hyper = "1.4.1"
|
||||
hyper-util = "0.1"
|
||||
indicatif = "0.17.8"
|
||||
@@ -269,24 +269,23 @@ ipnetwork = "0.20"
|
||||
isocountry = "0.3.2"
|
||||
itertools = "0.13.0"
|
||||
k256 = "0.13"
|
||||
lazy_static = "1.5.0"
|
||||
lazy_static = "1.4.0"
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
log = "0.4"
|
||||
maxminddb = "0.23.0"
|
||||
rs_merkle = "1.4.2"
|
||||
mime = "0.3.17"
|
||||
moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.20.2"
|
||||
once_cell = "1.7.2"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
parking_lot = "0.12.3"
|
||||
pem = "0.8"
|
||||
petgraph = "0.6.5"
|
||||
pin-project = "1.1"
|
||||
pin-project = "1.0"
|
||||
pin-project-lite = "0.2.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
publicsuffix = "2.2.3"
|
||||
@@ -306,11 +305,11 @@ rocket_okapi = "0.8.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.21"
|
||||
semver = "1.0.23"
|
||||
serde = "1.0.211"
|
||||
serde = "1.0.210"
|
||||
serde_bytes = "0.11.15"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.132"
|
||||
serde_json_path = "0.7.1"
|
||||
serde_json_path = "0.6.7"
|
||||
serde_repr = "0.1"
|
||||
serde_with = "3.9.0"
|
||||
serde_yaml = "0.9.25"
|
||||
@@ -325,7 +324,7 @@ syn = "1"
|
||||
sysinfo = "0.30.13"
|
||||
tap = "1.0.1"
|
||||
tar = "0.4.42"
|
||||
tempfile = "3.14"
|
||||
tempfile = "3.5.0"
|
||||
thiserror = "1.0.64"
|
||||
time = "0.3.30"
|
||||
tokio = "1.39"
|
||||
@@ -345,9 +344,9 @@ tracing-log = "0.2"
|
||||
ts-rs = "10.0.0"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
url = "2.5"
|
||||
utoipa = "5.2"
|
||||
utoipa-swagger-ui = "8.0"
|
||||
utoipauto = "0.2"
|
||||
utoipa = "4.2"
|
||||
utoipa-swagger-ui = "7.1"
|
||||
utoipauto = "0.1"
|
||||
uuid = "*"
|
||||
vergen = { version = "=8.3.1", default-features = false }
|
||||
walkdir = "2"
|
||||
@@ -403,10 +402,11 @@ indexed_db_futures = { git = "https://github.com/TiemenSch/rust-indexed-db", bra
|
||||
js-sys = "0.3.70"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
tsify = "0.4.5"
|
||||
wasm-bindgen = "0.2.95"
|
||||
wasm-bindgen-futures = "0.4.45"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-futures = "0.4.43"
|
||||
wasmtimer = "0.2.0"
|
||||
web-sys = "0.3.72"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
|
||||
# Profile settings for individual crates
|
||||
|
||||
@@ -416,6 +416,10 @@ web-sys = "0.3.72"
|
||||
[profile.dev.package.sqlx-macros]
|
||||
opt-level = 3
|
||||
|
||||
[profile.release.package.nym-socks5-listener]
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
||||
[profile.release.package.nym-client-wasm]
|
||||
# lto = true
|
||||
opt-level = 'z'
|
||||
|
||||
@@ -14,7 +14,6 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
|
||||
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
|
||||
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
||||
* `nym-cli` - a tool for interacting with the network from the CLI.
|
||||
<!-- coming soon
|
||||
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
||||
-->
|
||||
@@ -36,20 +35,24 @@ client ───► Gateway ──┘ mix │ mix ┌─►mix ───►
|
||||
### Building
|
||||
|
||||
* Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html).
|
||||
* Wallet build instructions are available [here](https://github.com/nymtech/nym/tree/master/nym-wallet#installation-prerequisites---linux--mac).
|
||||
* Wallet build instructions are available on Nym [Technical docs](https://nymtech.net/docs/wallet/desktop-wallet.html).
|
||||
|
||||
### Developing
|
||||
|
||||
There's a [`sandbox.env`](https://github.com/nymtech/nym/envs/sandbox.env) file provided which you can rename to `.env` if you want convenient testing environment. Read more about sandbox environment in our [Operators Guide page](https://nymtech.net/operators/sandbox.html).
|
||||
|
||||
References for developers:
|
||||
|
||||
* [Dev Docs](https://nymtech.net/docs/developers)
|
||||
* [SDKs](https://nymtech.net/docs/developers/rust)
|
||||
* [Network Docs](https://nymtech.net/docs/network)
|
||||
* [Release Cycle - git flow](https://nymtech.net/docs/operators/release-cycle)
|
||||
* [Developers Portal](https://nymtech.net/developers)
|
||||
* [Typescript SDKs](https://sdk.nymtech.net/)
|
||||
* [Technical Documentation - Nym network overview](https://nymtech.net/docs/)
|
||||
* [Release Cycle - git flow](https://nymtech.net/operators/release-cycle.html)
|
||||
|
||||
### Developer chat
|
||||
|
||||
You can chat to us in the #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) or on the [Nym Forum](https://forum.nymtech.net).
|
||||
You can chat to us in two places:
|
||||
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
|
||||
* The various developer channels on [Discord](https://nymtech.net/go/discord)
|
||||
|
||||
### Tokenomics & Rewards
|
||||
|
||||
|
||||
+56
-68
@@ -3,23 +3,37 @@ Critical bug or security issue 💥
|
||||
If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to:
|
||||
|
||||
```
|
||||
security@nym.com
|
||||
security@nymte.ch
|
||||
```
|
||||
|
||||
Encrypted with our public key which is available below in plain text and also on keyservers:
|
||||
|
||||
```
|
||||
sec rsa4096/7C3C727F05090550 2023-10-30 [SC] [expire : 2026-10-29]
|
||||
pub rsa4096 2023-10-30 [SC] [expire : 2026-10-29]
|
||||
24B2592E801A5AAA8666C8BA7C3C727F05090550
|
||||
uid [ ultime ] Security Nym Technologies <security@nym.com>
|
||||
ssb rsa4096/ACD0FBD79DC70ACC 2023-10-30 [E] [expire : 2026-10-29]
|
||||
uid [ ultime ] Security Nym Technologies <security@nymte.ch>
|
||||
sub rsa4096 2023-10-30 [E] [expire : 2026-10-29]
|
||||
|
||||
```
|
||||
|
||||
The fingerprint of the key is on the second line above.
|
||||
|
||||
If you need to chat __urgently__ to our team for a __critical__ security issue:
|
||||
|
||||
go to Matrix, and alert the core engineers with a private direct message:
|
||||
|
||||
Jedrzej Stuczynski @jstuczyn:nymtech.chat
|
||||
Mark Sinclair @mark:nymtech.chat
|
||||
Raphaël Walther @raphael:nymtech.chat
|
||||
|
||||
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
|
||||
|
||||
If you don't know what Matrix is, you can follow this documentation to create an account on this federation of instant messaging servers:
|
||||
|
||||
[Matrix for Instant Messaging](https://matrix.org/docs/chat_basics/matrix-for-im/)
|
||||
|
||||
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
@@ -34,69 +48,43 @@ vMFUIzBMHOPXH16036zGyFMC1esRd2qqil4b9KtLgCOkrD1VgpjcveoA0VyMJCN6
|
||||
LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A
|
||||
fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj
|
||||
IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB
|
||||
tCxTZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW0uY29tPokC
|
||||
VAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJnSd5VAhsDBQkFo5qABQsJ
|
||||
CAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQPPIP/ipGz2zLAjE2dSE3
|
||||
VcqOvras0DfqIL9HDm26Dg6QO2D/4YRntw0RqVyuy+zFnRUm+RZCKLPLUzbQ9Wjb
|
||||
G/Og5ttQVYQMu5eKu7OMvXkrbRo3teZFU+8IL08zIW6pyf9haxO6YMhLRy6cLYwW
|
||||
0EYC6Qzn5gz3kI7VkI8fWfs2Dk4XEV3D+SVtBoF6KRxMXT6HZvpzoMSEJZBoNj8S
|
||||
jw0TF8TFUQf49jUQbIHumukMswolrHi8a5ej8DSfNwSgz+Tt8oh5lu01kyUJiHn7
|
||||
nuHaY4Y9cHUVAOSwq/hovG52+ZE1r3aiswvle/B19o9pKeWWVvacSptGxDQagBtQ
|
||||
igoNLdRvY0XN2TEyX9pOHR0AoVOxtIW11CpkKuDbQG9vPwovqJ2L6+Fh3pzHYzcI
|
||||
2GIShNm/Z2SZBiUqbljJe9H4UAT/aHgMINkEG8qzUKwO42MA5HJT7YbHTR17/QSF
|
||||
Il5dhneRzmSbNcW2rdRwx/BmzrcsFJfqCt4JG/WDF293xSOjhFqQYvU4gCO+OB7o
|
||||
KXjX907XXDjS2KEJ71OGqVfk/P7BqEfQNfrLtb02TyXJAPQXHhybv23c4E7zUs9V
|
||||
lMjNizzxYB96uwJb0LAB2ijzEwoP91uGT2tFjk6F08x2QiArmXUdgrv44b39Stia
|
||||
gJS0GYKqSzyr10xHhUuDA+GKYtcitC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVz
|
||||
IDxzZWN1cml0eUBueW10ZS5jaD6JAjYEMAEKACAWIQQkslkugBpaqoZmyLp8PHJ/
|
||||
BQkFUAUCZ0nftQIdIAAKCRB8PHJ/BQkFUFHDEACtyNuUEjKCLAT5mSfow85PjFgo
|
||||
o8kHjQr/IIQ7ZbBOHeJJcrxDuypssiLh5XUjF3x5BiBfZ6vCxSb81RRwsDMp0mA1
|
||||
qzv9G8sgW0HTQUnZ9oH6CYut2NgzAnQpmuacrunm9Zy0FJ3ejbmwUY/NqK6gJkle
|
||||
66duHKhAy7DWjj7amd0C8bPDR+PA44fI3MezDHkQNaauKZTRqd1TqH8Qk5PAl4cB
|
||||
o5gVzeZh/U7/usvtGhazAIUF5BqK6bTmDnYopg+2x8jjwrG4+08GrttZkNjBLXeA
|
||||
Y/2U064yMz12LPv01qqAFdZ+coRy/ps/gOQTz34/VeW0CFy7TMqs4t3vSBWTqU7w
|
||||
hnw/qj6cM33fdxctj6KDgJSCkZdx2fvwXgxiPqUa5+j9FlFBeD5RDAl6g6t8N1/K
|
||||
Xca+zNYuSZgc297q1D+mtSD1C7uJNPxoAl+Bv5KNKpsjfQ+m04++CIFtGyX22aCA
|
||||
h2/tHwQZIXhOiMAKOoupidDVDhgxtCJ3Ps416xL0sTZfsPfg+j1Uv/Em9pzPClEl
|
||||
fX6+1O4DdSyZUQ4VsjMu/H5W/NQdbHgmqFrxQ6WX/0s5GMwO6GMDiPe8sOrwz9wD
|
||||
WYtyjafxXOHEZ1OjYX5gr7bGaG4oKc2btTJN0B3Phg4dStnHCNjEYccxuV3507fj
|
||||
HnNotkpXF2nGLxy+PYkCVAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJl
|
||||
P16XAhsDBQkFo5qABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQ
|
||||
vt0P/3M7ve4jlVH5JbE8WYlb71Cw+JBtVG8bA1m6ty2ZDazY97S8dEQsV6oPmhi9
|
||||
LHYt6q7civ7Of8LeQMyQ2zjShtIvqMIGIs+TERjQmrM8z2NmvsSO+WJtZXd/oPGi
|
||||
hEioTJ83nsjc4EaWpkTQzHuxC4JyBq7wLvQMX3nZCEpTBcLZyamYModUQSiKjXEZ
|
||||
IPh1CEZqkIHfZFAa2do4Z8A71x6U0y9KrQun5onF4AKZYVjlPj/Lv0k9xPm9Kz/I
|
||||
QhGZof9+famLBoUGwiqAckkzE7fvjQ/WEXyPspZShSh+4VoQAscGkSmNZM6Px6PX
|
||||
CYWW77kX6NJWhzvzVZtlle7Thw4FecZOiDNLEykF71O0tvbzN3pbMWi0LbkpMjem
|
||||
eAzNexAYsks7C+hkqJIsWM9GODWvChuYHdDcnZbkZhK96JrQIOxkY/6DzsZVV/+g
|
||||
PYOOlrrzHJVjAhh4DpPcM11LvuoeJG4aJLiRZfJWMOo4gJuhHi4K+3T3OWQG4sN1
|
||||
NapZgBFS9mO2OzjD86Wt2sEjcDzy2Lx9GV1ho2m7XswTWKWk3jjIqgm2qJZzdOKy
|
||||
Tx5gt/HpzsVAXc+tG9qavsVm+EWxgwvkJ40UdMTM3pjeOMo3/rQO8J+h1adYUrjt
|
||||
PGSXxQBcZjZb0HR9apDBRWvy4u4zLIyIpt6i5+LusY6PoAwSuQINBGU/XpcBEADw
|
||||
UaYPkne0zUE4zWeInFklN0Jq7MS3ESgEvRG6oYGRHxTb2xEzoPC7ONTs/CxwWcFo
|
||||
bJT55TvcbAaQfejMV8W2WViFCuE/+SQ37SsQ7leL6dH7gkIVGqt/eclcAA9+KSxR
|
||||
Eynx6vV1Q5jb97VlKgu1L6fNSK9XwJ8T6mCP/li0bA8L4pgcsBOQqQwQsKttbhQ7
|
||||
TpVY6pCy+vqSK9e4lBAt0QAykGUAOZcUbjkOY8pDOwFZSC9PDIbLctfSfKzxyCl7
|
||||
H0EfyGGvTVCYbI/jr2w3dw2u0W67xTpECyIIP/yaUG4PcRARTlIXSVzuQIgIBmT7
|
||||
ASWZeeKYSLRM5ixvPqT3SW16JxfvSsD0Y17Ijvof5VI5i9amRHBokCBR3OsAzWj1
|
||||
IcBKr5xnqcPzk5mKhCQNYzr5D6x/ah6fSomecaZSv3eQkgLQ8h2CtdGu2I195FrV
|
||||
n8fQuZETLIQygWJWK+fPW9WTzk6rpJ/eW1TC+YPO64k62ynZCnr7HpKef/Ipq4ji
|
||||
5/gJ//ApRyHivoHCCDkPDj2TLuXCWcAuKjgYhWgYukt5/3fBagOangqXKHdk9/ac
|
||||
HRT3W7dsdFSjub4PtAn3xzIq2osvOODuzMZ/rSkhY1ZGxp1hBPdtKdzKeqScCM5X
|
||||
cEK/Hr+K+yemP/9CNhEe2+JQBxDUMEHpQLqrbcUizQARAQABiQI8BBgBCgAmFiEE
|
||||
JLJZLoAaWqqGZsi6fDxyfwUJBVAFAmU/XpcCGwwFCQWjmoAACgkQfDxyfwUJBVDO
|
||||
6Q/+IvGM7FP5Sk9H/9ymsNVIuQ/ikXlubDfpTTuO+qlL78pNezGKJCV599Fps6k0
|
||||
tx34rW55q/K32ecjAce0dL2Qz0DsMHDcuekRteSLjpAMIIjqeIaKOUs4R4Baqehz
|
||||
Aw1NPTnBVWAK07cNA/YyhIJyoelcMKG87Kvl3nGNgtUmGVy0gxKiZxXMavKOSc+7
|
||||
RiP9OQL/hz6bzplZBQMeowdAi6IR6e9vrb8Xhzk4khFk8SfvTB112SAm4lTd4d0e
|
||||
ue6EKSMIL3cZAZsaPTdmqizrr33LqGY94PdTkhS/y6PBlhUIK4ObyAl+undHpYt9
|
||||
5l4r1aZNUGkLOwODd9WFLCY6TqmnwMCrX8N6/fJGQWCAITIYrpz1v5rQJEGdyXx7
|
||||
lDRBAjM2JL+4TFA/eH4Qsmzc/Qws/7oVaE1SvLT2feWdaSvWFj6/UzQYDFt92/+2
|
||||
wI3U+r9VATP4JOperJp+1aV04dbDoeH6C3DFweMbgwE6s7mggJDeWHaEbBDnxwOB
|
||||
lhIr84aAn0GCAWXUJwzS4YLT9NEIjXO3/jL7t59JlAxmQejtUL/cwVunaFw16Olb
|
||||
4HFW7ApO6cW9YrdMuGqxKVDnLKGpyGih6mwUer1uiwoWMskLTW6msG/WuKeg1247
|
||||
LjcVvJLsk9FGykA03RGILakxtZ0TRlNBoWrq08iDTnymOqI=
|
||||
=QPTf
|
||||
tC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW10ZS5jaD6J
|
||||
AlQEEwEKAD4WIQQkslkugBpaqoZmyLp8PHJ/BQkFUAUCZT9elwIbAwUJBaOagAUL
|
||||
CQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB8PHJ/BQkFUL7dD/9zO73uI5VR+SWx
|
||||
PFmJW+9QsPiQbVRvGwNZurctmQ2s2Pe0vHRELFeqD5oYvSx2Lequ3Ir+zn/C3kDM
|
||||
kNs40obSL6jCBiLPkxEY0JqzPM9jZr7EjvlibWV3f6DxooRIqEyfN57I3OBGlqZE
|
||||
0Mx7sQuCcgau8C70DF952QhKUwXC2cmpmDKHVEEoio1xGSD4dQhGapCB32RQGtna
|
||||
OGfAO9celNMvSq0Lp+aJxeACmWFY5T4/y79JPcT5vSs/yEIRmaH/fn2piwaFBsIq
|
||||
gHJJMxO3740P1hF8j7KWUoUofuFaEALHBpEpjWTOj8ej1wmFlu+5F+jSVoc781Wb
|
||||
ZZXu04cOBXnGTogzSxMpBe9TtLb28zd6WzFotC25KTI3pngMzXsQGLJLOwvoZKiS
|
||||
LFjPRjg1rwobmB3Q3J2W5GYSveia0CDsZGP+g87GVVf/oD2Djpa68xyVYwIYeA6T
|
||||
3DNdS77qHiRuGiS4kWXyVjDqOICboR4uCvt09zlkBuLDdTWqWYARUvZjtjs4w/Ol
|
||||
rdrBI3A88ti8fRldYaNpu17ME1ilpN44yKoJtqiWc3Tisk8eYLfx6c7FQF3PrRva
|
||||
mr7FZvhFsYML5CeNFHTEzN6Y3jjKN/60DvCfodWnWFK47Txkl8UAXGY2W9B0fWqQ
|
||||
wUVr8uLuMyyMiKbeoufi7rGOj6AMErkCDQRlP16XARAA8FGmD5J3tM1BOM1niJxZ
|
||||
JTdCauzEtxEoBL0RuqGBkR8U29sRM6DwuzjU7PwscFnBaGyU+eU73GwGkH3ozFfF
|
||||
tllYhQrhP/kkN+0rEO5Xi+nR+4JCFRqrf3nJXAAPfiksURMp8er1dUOY2/e1ZSoL
|
||||
tS+nzUivV8CfE+pgj/5YtGwPC+KYHLATkKkMELCrbW4UO06VWOqQsvr6kivXuJQQ
|
||||
LdEAMpBlADmXFG45DmPKQzsBWUgvTwyGy3LX0nys8cgpex9BH8hhr01QmGyP469s
|
||||
N3cNrtFuu8U6RAsiCD/8mlBuD3EQEU5SF0lc7kCICAZk+wElmXnimEi0TOYsbz6k
|
||||
90lteicX70rA9GNeyI76H+VSOYvWpkRwaJAgUdzrAM1o9SHASq+cZ6nD85OZioQk
|
||||
DWM6+Q+sf2oen0qJnnGmUr93kJIC0PIdgrXRrtiNfeRa1Z/H0LmREyyEMoFiVivn
|
||||
z1vVk85Oq6Sf3ltUwvmDzuuJOtsp2Qp6+x6Snn/yKauI4uf4Cf/wKUch4r6Bwgg5
|
||||
Dw49ky7lwlnALio4GIVoGLpLef93wWoDmp4Klyh3ZPf2nB0U91u3bHRUo7m+D7QJ
|
||||
98cyKtqLLzjg7szGf60pIWNWRsadYQT3bSncynqknAjOV3BCvx6/ivsnpj//QjYR
|
||||
HtviUAcQ1DBB6UC6q23FIs0AEQEAAYkCPAQYAQoAJhYhBCSyWS6AGlqqhmbIunw8
|
||||
cn8FCQVQBQJlP16XAhsMBQkFo5qAAAoJEHw8cn8FCQVQzukP/iLxjOxT+UpPR//c
|
||||
prDVSLkP4pF5bmw36U07jvqpS+/KTXsxiiQleffRabOpNLcd+K1ueavyt9nnIwHH
|
||||
tHS9kM9A7DBw3LnpEbXki46QDCCI6niGijlLOEeAWqnocwMNTT05wVVgCtO3DQP2
|
||||
MoSCcqHpXDChvOyr5d5xjYLVJhlctIMSomcVzGryjknPu0Yj/TkC/4c+m86ZWQUD
|
||||
HqMHQIuiEenvb62/F4c5OJIRZPEn70wdddkgJuJU3eHdHrnuhCkjCC93GQGbGj03
|
||||
Zqos6699y6hmPeD3U5IUv8ujwZYVCCuDm8gJfrp3R6WLfeZeK9WmTVBpCzsDg3fV
|
||||
hSwmOk6pp8DAq1/Dev3yRkFggCEyGK6c9b+a0CRBncl8e5Q0QQIzNiS/uExQP3h+
|
||||
ELJs3P0MLP+6FWhNUry09n3lnWkr1hY+v1M0GAxbfdv/tsCN1Pq/VQEz+CTqXqya
|
||||
ftWldOHWw6Hh+gtwxcHjG4MBOrO5oICQ3lh2hGwQ58cDgZYSK/OGgJ9BggFl1CcM
|
||||
0uGC0/TRCI1zt/4y+7efSZQMZkHo7VC/3MFbp2hcNejpW+BxVuwKTunFvWK3TLhq
|
||||
sSlQ5yyhqchooepsFHq9bosKFjLJC01uprBv1rinoNduOy43FbyS7JPRRspANN0R
|
||||
iC2pMbWdE0ZTQaFq6tPIg058pjqi
|
||||
=nqgX
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.45"
|
||||
version = "1.1.42"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -102,10 +102,5 @@ average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
|
||||
[debug.cover_traffic]
|
||||
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
||||
|
||||
[debug.stats_reporting]
|
||||
enabled = {{ debug.stats_reporting.enabled }}
|
||||
provider_address = '{{ debug.stats_reporting.provider_address }}'
|
||||
reporting_interval = '{{ debug.stats_reporting.reporting_interval }}'
|
||||
|
||||
|
||||
"#;
|
||||
|
||||
@@ -81,7 +81,6 @@ impl From<Init> for OverrideConfig {
|
||||
|
||||
nyxd_urls: init_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
stats_reporting_address: init_config.common_args.stats_reporting_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ use clap::{Parser, Subcommand};
|
||||
use log::{error, info};
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_client::client::Recipient;
|
||||
use nym_client_core::cli_helpers::CliClient;
|
||||
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
|
||||
use nym_config::OptionalSet;
|
||||
@@ -105,7 +104,6 @@ pub(crate) struct OverrideConfig {
|
||||
no_cover: bool,
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
stats_reporting_address: Option<Recipient>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -151,11 +149,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
BaseClientConfig::with_disabled_credentials,
|
||||
args.enabled_credentials_mode.map(|b| !b),
|
||||
)
|
||||
.with_optional_env_ext(
|
||||
BaseClientConfig::with_enabled_stats_reporting_address,
|
||||
args.stats_reporting_address,
|
||||
nym_network_defaults::var_names::CLIENT_STATS_COLLECTION_PROVIDER,
|
||||
)
|
||||
}
|
||||
|
||||
async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::{
|
||||
client::SocketClient,
|
||||
client::{config::Config, SocketClient},
|
||||
commands::{override_config, OverrideConfig},
|
||||
error::ClientError,
|
||||
};
|
||||
use clap::Args;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
@@ -40,7 +43,25 @@ impl From<Run> for OverrideConfig {
|
||||
no_cover: run_config.common_args.no_cover,
|
||||
nyxd_urls: run_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
stats_reporting_address: run_config.common_args.stats_reporting_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this only checks compatibility between config the binary. It does not take into consideration
|
||||
// network version. It might do so in the future.
|
||||
fn version_check(cfg: &Config) -> bool {
|
||||
let binary_version = env!("CARGO_PKG_VERSION");
|
||||
let config_version = &cfg.base.client.version;
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
true
|
||||
} else {
|
||||
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +72,11 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync
|
||||
let mut config = try_load_current_config(&args.common_args.id).await?;
|
||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return Err(Box::new(ClientError::FailedLocalVersionCheck));
|
||||
}
|
||||
|
||||
SocketClient::new(config, args.common_args.custom_mixnet)
|
||||
.run_socket_forever()
|
||||
.await
|
||||
|
||||
@@ -17,6 +17,9 @@ pub enum ClientError {
|
||||
#[error("Failed to validate the loaded config")]
|
||||
ConfigValidationFailure,
|
||||
|
||||
#[error("Failed local version check, client and config mismatch")]
|
||||
FailedLocalVersionCheck,
|
||||
|
||||
#[error("Attempted to start the client in invalid socket mode")]
|
||||
InvalidSocketMode,
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.45"
|
||||
version = "1.1.42"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -92,7 +92,6 @@ impl From<Init> for OverrideConfig {
|
||||
nyxd_urls: init_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
outfox: false,
|
||||
stats_reporting_address: init_config.common_args.stats_reporting_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
|
||||
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
use nym_client_core::config::{GroupBy, TopologyStructure};
|
||||
use nym_config::OptionalSet;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
@@ -112,7 +111,6 @@ pub(crate) struct OverrideConfig {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
outfox: bool,
|
||||
stats_reporting_address: Option<Recipient>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -198,11 +196,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
BaseClientConfig::with_disabled_credentials,
|
||||
args.enabled_credentials_mode.map(|b| !b),
|
||||
)
|
||||
.with_optional_base_env(
|
||||
BaseClientConfig::with_enabled_stats_reporting_address,
|
||||
args.stats_reporting_address,
|
||||
nym_network_defaults::var_names::CLIENT_STATS_COLLECTION_PROVIDER,
|
||||
)
|
||||
}
|
||||
|
||||
async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::commands::{override_config, OverrideConfig};
|
||||
use crate::config::Config;
|
||||
use crate::{
|
||||
commands::{override_config, OverrideConfig},
|
||||
error::Socks5ClientError,
|
||||
};
|
||||
use clap::Args;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
|
||||
use nym_client_core::client::base_client::storage::OnDiskPersistent;
|
||||
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
@@ -64,7 +70,6 @@ impl From<Run> for OverrideConfig {
|
||||
nyxd_urls: run_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
outfox: run_config.outfox,
|
||||
stats_reporting_address: run_config.common_args.stats_reporting_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,12 +81,38 @@ fn validate_country_group(s: &str) -> Result<CountryGroup, String> {
|
||||
}
|
||||
}
|
||||
|
||||
// this only checks compatibility between config the binary. It does not take into consideration
|
||||
// network version. It might do so in the future.
|
||||
fn version_check(cfg: &Config) -> bool {
|
||||
let binary_version = env!("CARGO_PKG_VERSION");
|
||||
let config_version = &cfg.core.base.client.version;
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!(
|
||||
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}",
|
||||
);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
true
|
||||
} else {
|
||||
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
eprintln!("Starting client {}...", args.common_args.id);
|
||||
|
||||
let mut config = try_load_current_config(&args.common_args.id).await?;
|
||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
|
||||
}
|
||||
|
||||
let storage =
|
||||
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
|
||||
.await?;
|
||||
|
||||
@@ -108,9 +108,4 @@ average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}'
|
||||
[core.debug.cover_traffic]
|
||||
loop_cover_traffic_average_delay = '{{ core.debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
||||
|
||||
[core.debug.stats_reporting]
|
||||
enabled = {{ core.debug.stats_reporting.enabled }}
|
||||
provider_address = '{{ core.debug.stats_reporting.provider_address }}'
|
||||
reporting_interval = '{{ core.debug.stats_reporting.reporting_interval }}'
|
||||
|
||||
"#;
|
||||
|
||||
@@ -14,6 +14,9 @@ pub enum Socks5ClientError {
|
||||
#[error("Failed to validate the loaded config")]
|
||||
ConfigValidationFailure,
|
||||
|
||||
#[error("Failed local version check, client and config mismatch")]
|
||||
FailedLocalVersionCheck,
|
||||
|
||||
#[error("Fail to bind address")]
|
||||
FailToBindAddress,
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ thiserror = { workspace = true }
|
||||
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-service-provider-requests-common = { path = "../service-provider-requests-common" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-wireguard-types = { path = "../wireguard-types" }
|
||||
|
||||
@@ -22,7 +22,4 @@ pub enum Error {
|
||||
|
||||
#[error("conversion: {0}")]
|
||||
Conversion(String),
|
||||
|
||||
#[error("failed to serialize response packet: {source}")]
|
||||
FailedToSerializeResponsePacket { source: Box<bincode::ErrorKind> },
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod traits;
|
||||
pub mod v1;
|
||||
pub mod v2;
|
||||
pub mod v3;
|
||||
pub mod v4;
|
||||
|
||||
mod error;
|
||||
mod util;
|
||||
|
||||
pub use error::Error;
|
||||
pub use v4 as latest;
|
||||
pub use v3 as latest;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = 4;
|
||||
pub const CURRENT_VERSION: u8 = 3;
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519::PrivateKey;
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
v1, v2, v3,
|
||||
v4::{self, registration::IpPair},
|
||||
Error,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum AuthenticatorVersion {
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
impl From<Protocol> for AuthenticatorVersion {
|
||||
fn from(value: Protocol) -> Self {
|
||||
if value.service_provider_type != ServiceProviderType::Authenticator {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
} else if value.version == v1::VERSION {
|
||||
AuthenticatorVersion::V1
|
||||
} else if value.version == v2::VERSION {
|
||||
AuthenticatorVersion::V2
|
||||
} else if value.version == v3::VERSION {
|
||||
AuthenticatorVersion::V3
|
||||
} else if value.version == v4::VERSION {
|
||||
AuthenticatorVersion::V4
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
}
|
||||
|
||||
impl InitMessage for v1::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v2::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v3::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v4::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn credential(&self) -> Option<CredentialSpendingData>;
|
||||
}
|
||||
|
||||
impl FinalMessage for v1::GatewayClient {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.private_ip.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v2::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ip.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v3::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ip.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v4::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ips
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait QueryBandwidthMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
}
|
||||
|
||||
impl QueryBandwidthMessage for PeerPublicKey {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn credential(&self) -> CredentialSpendingData;
|
||||
}
|
||||
|
||||
impl TopUpMessage for v3::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpMessage for v4::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AuthenticatorRequest {
|
||||
Initial {
|
||||
msg: Box<dyn InitMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
Final {
|
||||
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
QueryBandwidth {
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
TopUpBandwidth {
|
||||
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v1::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v1::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
|
||||
msg: Box::new(gateway_client),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v2::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v3::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v4::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
|
||||
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
|
||||
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
|
||||
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
|
||||
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
|
||||
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
|
||||
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
|
||||
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
|
||||
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
|
||||
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
|
||||
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
|
||||
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
|
||||
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
|
||||
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
|
||||
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
|
||||
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
|
||||
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
|
||||
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
|
||||
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
|
||||
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
|
||||
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
|
||||
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
|
||||
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
|
||||
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
|
||||
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
|
||||
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
|
||||
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
|
||||
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
|
||||
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
|
||||
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
|
||||
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
|
||||
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
|
||||
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
|
||||
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
|
||||
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
|
||||
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
|
||||
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
|
||||
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
|
||||
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
|
||||
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
|
||||
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
|
||||
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
|
||||
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
|
||||
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
|
||||
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
|
||||
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
|
||||
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
|
||||
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
|
||||
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
|
||||
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
|
||||
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
|
||||
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
|
||||
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
|
||||
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
|
||||
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
|
||||
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
|
||||
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
|
||||
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
|
||||
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
|
||||
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
|
||||
0, 0, 0, 0, 0, 1,
|
||||
];
|
||||
pub(crate) const RECIPIENT: &str = "CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f";
|
||||
}
|
||||
@@ -27,9 +27,9 @@ pub type HmacSha256 = Hmac<Sha256>;
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct InitMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
@@ -41,7 +41,7 @@ impl InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct FinalMessage {
|
||||
/// Gateway client data
|
||||
pub gateway_client: GatewayClient,
|
||||
@@ -50,28 +50,28 @@ pub struct FinalMessage {
|
||||
pub credential: Option<CredentialSpendingData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RegistrationData {
|
||||
pub nonce: u64,
|
||||
pub gateway_data: GatewayClient,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RegistredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
/// Gateway/Nym node can then verify pub_key payload using the same process
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct GatewayClient {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
@@ -147,7 +147,7 @@ impl GatewayClient {
|
||||
|
||||
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
|
||||
// TODO2: rely on our internal crypto/hmac
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientMac(Vec<u8>);
|
||||
|
||||
impl fmt::Display for ClientMac {
|
||||
|
||||
@@ -87,7 +87,7 @@ impl AuthenticatorRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum AuthenticatorRequestData {
|
||||
Initial(InitMessage),
|
||||
Final(Box<FinalMessage>),
|
||||
|
||||
@@ -100,28 +100,28 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum AuthenticatorResponseData {
|
||||
PendingRegistration(PendingRegistrationResponse),
|
||||
Registered(RegisteredResponse),
|
||||
RemainingBandwidth(RemainingBandwidthResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PendingRegistrationResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistrationData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RemainingBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
|
||||
@@ -9,7 +9,7 @@ impl From<v2::request::AuthenticatorRequest> for v3::request::AuthenticatorReque
|
||||
fn from(authenticator_request: v2::request::AuthenticatorRequest) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
version: 3,
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: authenticator_request.data.into(),
|
||||
@@ -19,24 +19,6 @@ impl From<v2::request::AuthenticatorRequest> for v3::request::AuthenticatorReque
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v3::request::AuthenticatorRequest> for v2::request::AuthenticatorRequest {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_request: v3::request::AuthenticatorRequest,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
protocol: Protocol {
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: authenticator_request.data.try_into()?,
|
||||
reply_to: authenticator_request.reply_to,
|
||||
request_id: authenticator_request.request_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData {
|
||||
fn from(authenticator_request_data: v2::request::AuthenticatorRequestData) -> Self {
|
||||
match authenticator_request_data {
|
||||
@@ -53,29 +35,6 @@ impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorR
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v3::request::AuthenticatorRequestData> for v2::request::AuthenticatorRequestData {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_request_data: v3::request::AuthenticatorRequestData,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match authenticator_request_data {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
|
||||
v2::request::AuthenticatorRequestData::Initial(init_msg.into()),
|
||||
),
|
||||
v3::request::AuthenticatorRequestData::Final(gw_client) => Ok(
|
||||
v2::request::AuthenticatorRequestData::Final(gw_client.into()),
|
||||
),
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
|
||||
),
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(_) => Err(
|
||||
Self::Error::Conversion("no top up bandwidth variant in v2".to_string()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
|
||||
fn from(init_msg: v2::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
@@ -84,14 +43,6 @@ impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::InitMessage> for v2::registration::InitMessage {
|
||||
fn from(init_msg: v3::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
pub_key: init_msg.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMessage> {
|
||||
fn from(gw_client: Box<v2::registration::FinalMessage>) -> Self {
|
||||
Box::new(v3::registration::FinalMessage {
|
||||
@@ -101,15 +52,6 @@ impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMe
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v3::registration::FinalMessage>> for Box<v2::registration::FinalMessage> {
|
||||
fn from(gw_client: Box<v3::registration::FinalMessage>) -> Self {
|
||||
Box::new(v2::registration::FinalMessage {
|
||||
gateway_client: gw_client.gateway_client.into(),
|
||||
credential: gw_client.credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::GatewayClient> for v3::registration::GatewayClient {
|
||||
fn from(gw_client: v2::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
@@ -151,10 +93,7 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
|
||||
Ok(Self {
|
||||
data: authenticator_response.data.try_into()?,
|
||||
reply_to: authenticator_response.reply_to,
|
||||
protocol: Protocol {
|
||||
version: 2,
|
||||
service_provider_type: authenticator_response.protocol.service_provider_type,
|
||||
},
|
||||
protocol: authenticator_response.protocol,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -162,10 +101,7 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
|
||||
impl From<v2::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse {
|
||||
fn from(value: v2::response::AuthenticatorResponse) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
version: 3,
|
||||
service_provider_type: value.protocol.service_provider_type,
|
||||
},
|
||||
protocol: value.protocol,
|
||||
data: value.data.into(),
|
||||
reply_to: value.reply_to,
|
||||
}
|
||||
@@ -334,511 +270,3 @@ impl From<v2::registration::RemainingBandwidthData> for v3::registration::Remain
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{net::IpAddr, str::FromStr};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use super::*;
|
||||
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
|
||||
|
||||
#[test]
|
||||
fn upgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v2::request::AuthenticatorRequest::new_initial_request(
|
||||
v2::registration::InitMessage::new(pub_key),
|
||||
reply_to,
|
||||
);
|
||||
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
|
||||
v3::registration::InitMessage::new(pub_key),
|
||||
reply_to,
|
||||
);
|
||||
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v2::request::AuthenticatorRequestData::Initial(v2::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let nonce = 42;
|
||||
let gateway_client = v2::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
);
|
||||
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
|
||||
let final_message = v2::registration::FinalMessage {
|
||||
gateway_client,
|
||||
credential: credential.clone(),
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v2::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
|
||||
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v3::request::AuthenticatorRequestData::Final(Box::new(
|
||||
v3::registration::FinalMessage {
|
||||
gateway_client: v3::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
),
|
||||
credential
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let nonce = 42;
|
||||
let gateway_client = v3::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
);
|
||||
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
|
||||
let final_message = v3::registration::FinalMessage {
|
||||
gateway_client,
|
||||
credential: credential.clone(),
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
|
||||
let upgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v2::request::AuthenticatorRequestData::Final(Box::new(
|
||||
v2::registration::FinalMessage {
|
||||
gateway_client: v2::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
),
|
||||
credential
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v2::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
|
||||
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
|
||||
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_topup_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
|
||||
let top_up_message = v3::topup::TopUpMessage {
|
||||
pub_key,
|
||||
credential,
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v3::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
|
||||
assert!(v2::request::AuthenticatorRequest::try_from(msg).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v2::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v2::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v2::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
reply_to,
|
||||
);
|
||||
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v3::response::AuthenticatorResponseData::PendingRegistration(
|
||||
v3::response::PendingRegistrationResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v3::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data: v3::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
),
|
||||
wg_port,
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v3::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v3::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
reply_to,
|
||||
);
|
||||
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v2::response::AuthenticatorResponseData::PendingRegistration(
|
||||
v2::response::PendingRegistrationResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v2::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data: v2::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ip,
|
||||
nonce,
|
||||
),
|
||||
wg_port,
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let wg_port = 51822;
|
||||
let registred_data = v2::registration::RegistredData {
|
||||
pub_key,
|
||||
private_ip,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v2::response::AuthenticatorResponse::new_registered(
|
||||
registred_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v3::registration::RegistredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let wg_port = 51822;
|
||||
let registred_data = v3::registration::RegistredData {
|
||||
pub_key,
|
||||
private_ip,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v3::response::AuthenticatorResponse::new_registered(
|
||||
registred_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v2::registration::RegistredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v2::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v2::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v3::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v3::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: Some(v3::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 2,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v2::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v2::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: Some(v2::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_topup_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = v3::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v3::response::AuthenticatorResponse::new_topup_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
assert!(v2::response::AuthenticatorResponse::try_from(msg).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct InitMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
@@ -41,7 +41,7 @@ impl InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct FinalMessage {
|
||||
/// Gateway client data
|
||||
pub gateway_client: GatewayClient,
|
||||
@@ -50,28 +50,28 @@ pub struct FinalMessage {
|
||||
pub credential: Option<CredentialSpendingData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RegistrationData {
|
||||
pub nonce: u64,
|
||||
pub gateway_data: GatewayClient,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RegistredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
/// Gateway/Nym node can then verify pub_key payload using the same process
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct GatewayClient {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
@@ -147,7 +147,7 @@ impl GatewayClient {
|
||||
|
||||
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
|
||||
// TODO2: rely on our internal crypto/hmac
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientMac(Vec<u8>);
|
||||
|
||||
impl fmt::Display for ClientMac {
|
||||
|
||||
@@ -106,7 +106,7 @@ impl AuthenticatorRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum AuthenticatorRequestData {
|
||||
Initial(InitMessage),
|
||||
Final(Box<FinalMessage>),
|
||||
|
||||
@@ -120,7 +120,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum AuthenticatorResponseData {
|
||||
PendingRegistration(PendingRegistrationResponse),
|
||||
Registered(RegisteredResponse),
|
||||
@@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData {
|
||||
TopUpBandwidth(TopUpBandwidthResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PendingRegistrationResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistrationData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RemainingBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: Option<RemainingBandwidthData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TopUpBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
|
||||
@@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TopUpMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
@@ -1,737 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
use crate::{v3, v4};
|
||||
|
||||
impl TryFrom<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest {
|
||||
type Error = crate::Error;
|
||||
fn try_from(
|
||||
authenticator_request: v3::request::AuthenticatorRequest,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
protocol: Protocol {
|
||||
version: 4,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: authenticator_request.data.try_into()?,
|
||||
reply_to: authenticator_request.reply_to,
|
||||
request_id: authenticator_request.request_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v4::request::AuthenticatorRequest> for v3::request::AuthenticatorRequest {
|
||||
type Error = crate::Error;
|
||||
fn try_from(
|
||||
authenticator_request: v4::request::AuthenticatorRequest,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
protocol: Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: authenticator_request.data.try_into()?,
|
||||
reply_to: authenticator_request.reply_to,
|
||||
request_id: authenticator_request.request_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v3::request::AuthenticatorRequestData> for v4::request::AuthenticatorRequestData {
|
||||
type Error = crate::Error;
|
||||
fn try_from(
|
||||
authenticator_request_data: v3::request::AuthenticatorRequestData,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match authenticator_request_data {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
|
||||
v4::request::AuthenticatorRequestData::Initial(init_msg.into()),
|
||||
),
|
||||
v3::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion(
|
||||
"mac hash breaking change".to_string(),
|
||||
)),
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
|
||||
),
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v4::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData {
|
||||
type Error = crate::Error;
|
||||
fn try_from(
|
||||
authenticator_request_data: v4::request::AuthenticatorRequestData,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match authenticator_request_data {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
|
||||
v3::request::AuthenticatorRequestData::Initial(init_msg.into()),
|
||||
),
|
||||
v4::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion(
|
||||
"mac hash breaking change".to_string(),
|
||||
)),
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
|
||||
),
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::InitMessage> for v4::registration::InitMessage {
|
||||
fn from(init_msg: v3::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
pub_key: init_msg.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::InitMessage> for v3::registration::InitMessage {
|
||||
fn from(init_msg: v4::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
pub_key: init_msg.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v3::topup::TopUpMessage>> for Box<v4::topup::TopUpMessage> {
|
||||
fn from(top_up_message: Box<v3::topup::TopUpMessage>) -> Self {
|
||||
Box::new(v4::topup::TopUpMessage {
|
||||
pub_key: top_up_message.pub_key,
|
||||
credential: top_up_message.credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v4::topup::TopUpMessage>> for Box<v3::topup::TopUpMessage> {
|
||||
fn from(top_up_message: Box<v4::topup::TopUpMessage>) -> Self {
|
||||
Box::new(v3::topup::TopUpMessage {
|
||||
pub_key: top_up_message.pub_key,
|
||||
credential: top_up_message.credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v3::response::AuthenticatorResponse> for v4::response::AuthenticatorResponse {
|
||||
type Error = crate::Error;
|
||||
fn try_from(value: v3::response::AuthenticatorResponse) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
protocol: Protocol {
|
||||
version: 4,
|
||||
service_provider_type: value.protocol.service_provider_type,
|
||||
},
|
||||
data: value.data.try_into()?,
|
||||
reply_to: value.reply_to,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v4::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_response: v4::response::AuthenticatorResponse,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
data: authenticator_response.data.try_into()?,
|
||||
reply_to: authenticator_response.reply_to,
|
||||
protocol: Protocol {
|
||||
version: 3,
|
||||
service_provider_type: authenticator_response.protocol.service_provider_type,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v3::response::AuthenticatorResponseData> for v4::response::AuthenticatorResponseData {
|
||||
type Error = crate::Error;
|
||||
fn try_from(
|
||||
authenticator_response_data: v3::response::AuthenticatorResponseData,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match authenticator_response_data {
|
||||
v3::response::AuthenticatorResponseData::PendingRegistration(_) => Err(
|
||||
Self::Error::Conversion("mac hash breaking change".to_string()),
|
||||
),
|
||||
|
||||
v3::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
|
||||
v4::response::AuthenticatorResponseData::Registered(registered_response.into()),
|
||||
),
|
||||
|
||||
v3::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Ok(v4::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response.into(),
|
||||
)),
|
||||
v3::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => Ok(
|
||||
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_response_data: v4::response::AuthenticatorResponseData,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match authenticator_response_data {
|
||||
v4::response::AuthenticatorResponseData::PendingRegistration(_) => Err(
|
||||
Self::Error::Conversion("mac hash breaking change".to_string()),
|
||||
),
|
||||
|
||||
v4::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
|
||||
v3::response::AuthenticatorResponseData::Registered(registered_response.into()),
|
||||
),
|
||||
v4::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Ok(v3::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response.into(),
|
||||
)),
|
||||
v4::response::AuthenticatorResponseData::TopUpBandwidth(_) => {
|
||||
Err(Self::Error::Conversion(
|
||||
"a v3 request couldn't produce a v4 only type of response".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse {
|
||||
fn from(value: v4::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::response::RegisteredResponse> for v4::response::RegisteredResponse {
|
||||
fn from(value: v3::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::response::RemainingBandwidthResponse> for v4::response::RemainingBandwidthResponse {
|
||||
fn from(value: v3::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse {
|
||||
fn from(value: v4::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::response::TopUpBandwidthResponse> for v4::response::TopUpBandwidthResponse {
|
||||
fn from(value: v3::response::TopUpBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::TopUpBandwidthResponse> for v3::response::TopUpBandwidthResponse {
|
||||
fn from(value: v4::response::TopUpBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
|
||||
fn from(value: v3::registration::RegistredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ip.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistredData> for v3::registration::RegistredData {
|
||||
fn from(value: v4::registration::RegistredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ips.ipv4.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RemainingBandwidthData> for v4::registration::RemainingBandwidthData {
|
||||
fn from(value: v3::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData {
|
||||
fn from(value: v4::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use super::*;
|
||||
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
|
||||
|
||||
#[test]
|
||||
fn upgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
|
||||
v3::registration::InitMessage::new(pub_key),
|
||||
reply_to,
|
||||
);
|
||||
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 4,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v4::request::AuthenticatorRequestData::Initial(v4::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v4::request::AuthenticatorRequest::new_initial_request(
|
||||
v4::registration::InitMessage::new(pub_key),
|
||||
reply_to,
|
||||
);
|
||||
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let nonce = 42;
|
||||
let gateway_client = v3::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ipv4.into(),
|
||||
nonce,
|
||||
);
|
||||
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
|
||||
let final_message = v3::registration::FinalMessage {
|
||||
gateway_client,
|
||||
credential: credential.clone(),
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
|
||||
assert!(v4::request::AuthenticatorRequest::try_from(msg).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
|
||||
let nonce = 42;
|
||||
let gateway_client = v4::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ips,
|
||||
nonce,
|
||||
);
|
||||
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
|
||||
let final_message = v4::registration::FinalMessage {
|
||||
gateway_client,
|
||||
credential: credential.clone(),
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v4::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
|
||||
assert!(v3::request::AuthenticatorRequest::try_from(msg).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
|
||||
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 4,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v4::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
|
||||
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_topup_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
|
||||
let top_up_message = v4::topup::TopUpMessage {
|
||||
pub_key,
|
||||
credential: credential.clone(),
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v4::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
|
||||
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(Box::new(
|
||||
v3::topup::TopUpMessage {
|
||||
pub_key,
|
||||
credential
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v3::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ipv4.into(),
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v3::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
reply_to,
|
||||
);
|
||||
assert!(v4::response::AuthenticatorResponse::try_from(msg).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v4::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
private_ips,
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v4::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
reply_to,
|
||||
);
|
||||
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap());
|
||||
let wg_port = 51822;
|
||||
let registred_data = v3::registration::RegistredData {
|
||||
pub_key,
|
||||
private_ip: ipv4.into(),
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v3::response::AuthenticatorResponse::new_registered(
|
||||
registred_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 4,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v4::registration::RegistredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
|
||||
let wg_port = 51822;
|
||||
let registred_data = v4::registration::RegistredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_registered(
|
||||
registred_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v3::registration::RegistredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip: ipv4.into()
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 4,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v4::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v4::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: Some(v4::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v4::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
downgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 3,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
downgraded_msg.data,
|
||||
v3::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v3::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: Some(v3::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downgrade_topup_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = v4::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_topup_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod conversion;
|
||||
pub mod registration;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod topup;
|
||||
|
||||
pub const VERSION: u8 = 4;
|
||||
@@ -1,287 +0,0 @@
|
||||
// -2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::time::SystemTime;
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
use hmac::{Hmac, Mac};
|
||||
#[cfg(feature = "verify")]
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
#[cfg(feature = "verify")]
|
||||
use sha2::Sha256;
|
||||
|
||||
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
|
||||
pub type PrivateIPs = HashMap<IpPair, Taken>;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct IpPair {
|
||||
pub ipv4: Ipv4Addr,
|
||||
pub ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
impl IpPair {
|
||||
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
|
||||
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IpPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.ipv4, self.ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IpAddr> for IpPair {
|
||||
fn from(value: IpAddr) -> Self {
|
||||
let (before_last_byte, last_byte) = match value {
|
||||
std::net::IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]),
|
||||
std::net::IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]),
|
||||
};
|
||||
let last_bytes = (before_last_byte as u16) << 8 | last_byte as u16;
|
||||
let ipv4 = Ipv4Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1],
|
||||
before_last_byte,
|
||||
last_byte,
|
||||
);
|
||||
let ipv6 = Ipv6Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[1],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[2],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[3],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[4],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[5],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[6],
|
||||
last_bytes,
|
||||
);
|
||||
IpPair::new(ipv4, ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InitMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
}
|
||||
|
||||
impl InitMessage {
|
||||
pub fn new(pub_key: PeerPublicKey) -> Self {
|
||||
InitMessage { pub_key }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct FinalMessage {
|
||||
/// Gateway client data
|
||||
pub gateway_client: GatewayClient,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: Option<CredentialSpendingData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistrationData {
|
||||
pub nonce: u64,
|
||||
pub gateway_data: GatewayClient,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
/// Gateway/Nym node can then verify pub_key payload using the same process
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct GatewayClient {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Assigned private IPs (v4 and v6)
|
||||
pub private_ips: IpPair,
|
||||
|
||||
/// Sha256 hmac on the data (alongside the prior nonce)
|
||||
pub mac: ClientMac,
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn new(
|
||||
local_secret: &PrivateKey,
|
||||
remote_public: x25519_dalek::PublicKey,
|
||||
private_ips: IpPair,
|
||||
nonce: u64,
|
||||
) -> Self {
|
||||
// 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 dh = static_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())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(local_public.as_bytes());
|
||||
mac.update(private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
GatewayClient {
|
||||
pub_key: PeerPublicKey::new(local_public),
|
||||
private_ips,
|
||||
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable secret should be gateways Wireguard PK
|
||||
// Client should perform this step when generating its payload, using its own WG PK
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
// 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);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
mac.verify_slice(&self.mac)
|
||||
.map_err(|source| Error::FailedClientMacVerification {
|
||||
client: self.pub_key.to_string(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
|
||||
// TODO2: rely on our internal crypto/hmac
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ClientMac(Vec<u8>);
|
||||
|
||||
impl fmt::Display for ClientMac {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", general_purpose::STANDARD.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMac {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(mac: Vec<u8>) -> Self {
|
||||
ClientMac(mac)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientMac {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientMac {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mac_bytes: Vec<u8> =
|
||||
general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|source| Error::MalformedClientMac {
|
||||
mac: s.to_string(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
Ok(ClientMac(mac_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientMac {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMac {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
|
||||
#[test]
|
||||
fn create_ip_pair() {
|
||||
let ipv4: IpAddr = Ipv4Addr::from_str("10.1.10.50").unwrap().into();
|
||||
let ipv6: IpAddr = Ipv6Addr::from_str("fc01::0a32").unwrap().into();
|
||||
|
||||
assert_eq!(IpPair::from(ipv4), IpPair::from(ipv6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "verify")]
|
||||
fn client_request_roundtrip() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let gateway_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
let client_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
|
||||
let nonce = 1234567890;
|
||||
|
||||
let client = GatewayClient::new(
|
||||
client_key_pair.private_key(),
|
||||
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
|
||||
IpPair::new("10.0.0.42".parse().unwrap(), "fc00::42".parse().unwrap()),
|
||||
nonce,
|
||||
);
|
||||
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{
|
||||
registration::{FinalMessage, InitMessage},
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::VERSION;
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorRequest {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorRequestData,
|
||||
pub reply_to: Recipient,
|
||||
pub request_id: u64,
|
||||
}
|
||||
|
||||
impl AuthenticatorRequest {
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn new_initial_request(init_message: InitMessage, reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::Initial(init_message),
|
||||
reply_to,
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_final_request(final_message: FinalMessage, reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::Final(Box::new(final_message)),
|
||||
reply_to,
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_query_request(peer_public_key: PeerPublicKey, reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
|
||||
reply_to,
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_topup_request(top_up_message: TopUpMessage, reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::TopUpBandwidth(Box::new(top_up_message)),
|
||||
reply_to,
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorRequestData {
|
||||
Initial(InitMessage),
|
||||
Final(Box<FinalMessage>),
|
||||
QueryBandwidth(PeerPublicKey),
|
||||
TopUpBandwidth(Box<TopUpMessage>),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn check_first_bytes_protocol() {
|
||||
let version = 4;
|
||||
let data = AuthenticatorRequest {
|
||||
protocol: Protocol { version, service_provider_type: ServiceProviderType::Authenticator },
|
||||
data: AuthenticatorRequestData::Initial(InitMessage::new(
|
||||
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
|
||||
)),
|
||||
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
|
||||
request_id: 1,
|
||||
};
|
||||
let bytes = *data.to_bytes().unwrap().first_chunk::<2>().unwrap();
|
||||
assert_eq!(bytes, [version, ServiceProviderType::Authenticator as u8]);
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::VERSION;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorResponse {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorResponseData,
|
||||
pub reply_to: Recipient,
|
||||
}
|
||||
|
||||
impl AuthenticatorResponse {
|
||||
pub fn new_pending_registration_success(
|
||||
registration_data: RegistrationData,
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::PendingRegistration(PendingRegistrationResponse {
|
||||
reply: registration_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
}),
|
||||
reply_to,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::Registered(RegisteredResponse {
|
||||
reply: registred_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
}),
|
||||
reply_to,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_remaining_bandwidth(
|
||||
remaining_bandwidth_data: Option<RemainingBandwidthData>,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
}),
|
||||
reply_to,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_topup_bandwidth(
|
||||
remaining_bandwidth_data: RemainingBandwidthData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::TopUpBandwidth(TopUpBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
}),
|
||||
reply_to,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> Recipient {
|
||||
self.reply_to
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::TopUpBandwidth(response) => Some(response.request_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorResponseData {
|
||||
PendingRegistration(PendingRegistrationResponse),
|
||||
Registered(RegisteredResponse),
|
||||
RemainingBandwidth(RemainingBandwidthResponse),
|
||||
TopUpBandwidth(TopUpBandwidthResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingRegistrationResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistrationData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RemainingBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: Option<RemainingBandwidthData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TopUpBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RemainingBandwidthData,
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct TopUpMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: CredentialSpendingData,
|
||||
}
|
||||
@@ -14,15 +14,14 @@ thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "stream_cipher", "aes", "hashing"] }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-task = { path = "../task" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
|
||||
path = "../client-libs/validator-client"
|
||||
|
||||
@@ -17,7 +17,7 @@ use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::EcashSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient};
|
||||
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
|
||||
use nym_validator_client::EcashApiClient;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[derive(Debug)]
|
||||
// See other comments for other TaskStatus message enumds about abusing the Error trait when we
|
||||
// should have a new trait for TaskStatus messages
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum BandwidthStatusMessage {
|
||||
#[error("remaining bandwidth: {0}")]
|
||||
RemainingBandwidth(i64),
|
||||
|
||||
#[error("no bandwidth left")]
|
||||
NoBandwidth,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BandwidthStatusMessage {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
BandwidthStatusMessage::RemainingBandwidth(b) => {
|
||||
write!(f, "remaining bandwidth: {}", b)
|
||||
}
|
||||
BandwidthStatusMessage::NoBandwidth => write!(f, "no bandwidth left"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl nym_task::TaskStatusEvent for BandwidthStatusMessage {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ const-str = { workspace = true }
|
||||
log = { workspace = true }
|
||||
pretty_env_logger = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
semver.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
@@ -43,5 +44,5 @@ tracing = [
|
||||
"tracing-opentelemetry",
|
||||
"opentelemetry",
|
||||
]
|
||||
clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"]
|
||||
clap = [ "dep:clap", "dep:clap_complete", "dep:clap_complete_fig" ]
|
||||
models = []
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
pub mod build_information;
|
||||
pub mod logging;
|
||||
pub mod version_checker;
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
pub mod completions;
|
||||
|
||||
@@ -47,7 +47,6 @@ pub fn setup_logging() {
|
||||
#[cfg(feature = "basic_tracing")]
|
||||
pub fn setup_tracing_logger() {
|
||||
let log_builder = tracing_subscriber::fmt()
|
||||
.with_writer(std::io::stderr)
|
||||
// Use a more compact, abbreviated log format
|
||||
.compact()
|
||||
// Display source code file paths
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use semver::Version;
|
||||
|
||||
/// Checks if the version is minor version compatible.
|
||||
///
|
||||
/// Checks whether given `version` is compatible with a given semantic version requirement `req`
|
||||
/// according to major-minor semver rules. The semantic version requirement can be passed as a full,
|
||||
/// concrete version number, because that's what we'll have in our Cargo.toml files (e.g. 0.3.2).
|
||||
/// The patch number in the requirement gets dropped and replaced with a wildcard (0.3.*) as all
|
||||
/// minor versions should be compatible with each other.
|
||||
pub fn is_minor_version_compatible(version: &str, req: &str) -> bool {
|
||||
let expected_version = match Version::parse(version) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let req_version = match Version::parse(req) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
expected_version.major == req_version.major && expected_version.minor == req_version.minor
|
||||
}
|
||||
|
||||
pub fn parse_version(raw_version: &str) -> Result<Version, semver::Error> {
|
||||
Version::parse(raw_version)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn version_0_3_0_is_compatible_with_requirement_0_3_x() {
|
||||
assert!(is_minor_version_compatible("0.3.0", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_3_1_is_compatible_with_minimum_requirement_0_3_x() {
|
||||
assert!(is_minor_version_compatible("0.3.1", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_3_2_is_compatible_with_minimum_requirement_0_3_x() {
|
||||
assert!(is_minor_version_compatible("0.3.2", "0.3.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_2_0_is_not_compatible_with_requirement_0_3_x() {
|
||||
assert!(!is_minor_version_compatible("0.2.0", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_4_0_is_not_compatible_with_requirement_0_3_x() {
|
||||
assert!(!is_minor_version_compatible("0.4.0", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_1_3_2_is_not_compatible_with_requirement_0_3_x() {
|
||||
assert!(!is_minor_version_compatible("1.3.2", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_4_0_rc_1_is_compatible_with_version_0_4_0_rc_1() {
|
||||
assert!(is_minor_version_compatible("0.4.0-rc.1", "0.4.0-rc.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_false_on_foo_version() {
|
||||
assert!(!is_minor_version_compatible("foo", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_false_on_bar_version() {
|
||||
assert!(!is_minor_version_compatible("0.3.2", "bar"));
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,8 @@ nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-metrics = { path = "../nym-metrics" }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-statistics-common = { path = "../statistics" }
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
nym-topology = { path = "../topology", features = ["serializable"] }
|
||||
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-task = { path = "../task" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
|
||||
@@ -23,4 +23,4 @@ nym-sphinx-addressing = { path = "../../nymsphinx/addressing" }
|
||||
|
||||
|
||||
[features]
|
||||
disk-persistence = ["nym-pemstore"]
|
||||
disk-persistence = ["nym-pemstore"]
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_config::defaults::NymNetworkDetails;
|
||||
use nym_config::serde_helpers::{de_maybe_stringified, ser_maybe_stringified};
|
||||
use nym_sphinx_addressing::Recipient;
|
||||
use nym_sphinx_params::{PacketSize, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -62,11 +61,6 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
|
||||
// 24 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
|
||||
|
||||
// stats reporting related
|
||||
|
||||
/// Time interval between reporting statistics to the given provider if it exist
|
||||
const STATS_REPORT_INTERVAL_SECS: Duration = Duration::from_secs(300);
|
||||
|
||||
use crate::error::InvalidTrafficModeFailure;
|
||||
pub use nym_country_group::CountryGroup;
|
||||
|
||||
@@ -139,12 +133,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_enabled_stats_reporting_address(mut self, address: Recipient) -> Self {
|
||||
self.debug.stats_reporting.provider_address = Some(address);
|
||||
self.debug.stats_reporting.enabled = true; //since we are overriding the address, we assume the reporting should be enabled
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: this should be refactored properly
|
||||
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
|
||||
// medium_toggle:
|
||||
@@ -393,20 +381,13 @@ pub struct Traffic {
|
||||
/// poisson distribution.
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
pub deterministic_route_selection: bool,
|
||||
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
/// None - no limit
|
||||
pub maximum_number_of_retransmissions: Option<u32>,
|
||||
|
||||
/// Specifies the packet size used for sent messages.
|
||||
/// Do not override it unless you understand the consequences of that change.
|
||||
pub primary_packet_size: PacketSize,
|
||||
|
||||
/// Specifies the optional auxiliary packet size for optimizing message streams.
|
||||
/// Note that its use decreases overall anonymity.
|
||||
/// Do not set it unless you understand the consequences of that change.
|
||||
/// Do not set it it unless you understand the consequences of that change.
|
||||
pub secondary_packet_size: Option<PacketSize>,
|
||||
|
||||
pub packet_type: PacketType,
|
||||
@@ -431,8 +412,6 @@ impl Default for Traffic {
|
||||
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||
disable_main_poisson_packet_distribution: false,
|
||||
deterministic_route_selection: false,
|
||||
maximum_number_of_retransmissions: None,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
secondary_packet_size: None,
|
||||
packet_type: PacketType::Mix,
|
||||
@@ -652,34 +631,6 @@ impl Default for ReplySurbs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct StatsReporting {
|
||||
/// Is stats reporting enabled
|
||||
pub enabled: bool,
|
||||
|
||||
/// Address of the stats collector. If this is none, no reporting will happen, regardless of `enabled`
|
||||
#[serde(
|
||||
serialize_with = "ser_maybe_stringified",
|
||||
deserialize_with = "de_maybe_stringified"
|
||||
)]
|
||||
pub provider_address: Option<Recipient>,
|
||||
|
||||
/// With what frequence will statistics be sent
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub reporting_interval: Duration,
|
||||
}
|
||||
|
||||
impl Default for StatsReporting {
|
||||
fn default() -> Self {
|
||||
StatsReporting {
|
||||
enabled: true,
|
||||
provider_address: None,
|
||||
reporting_interval: STATS_REPORT_INTERVAL_SECS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct DebugConfig {
|
||||
@@ -700,9 +651,6 @@ pub struct DebugConfig {
|
||||
|
||||
/// Defines all configuration options related to reply SURBs.
|
||||
pub reply_surbs: ReplySurbs,
|
||||
|
||||
/// Defines all configuration options related to stats reporting.
|
||||
pub stats_reporting: StatsReporting,
|
||||
}
|
||||
|
||||
impl DebugConfig {
|
||||
@@ -724,7 +672,6 @@ impl Default for DebugConfig {
|
||||
acknowledgements: Default::default(),
|
||||
topology: Default::default(),
|
||||
reply_surbs: Default::default(),
|
||||
stats_reporting: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,6 @@ impl From<ConfigV5> for Config {
|
||||
primary_packet_size: value.debug.traffic.primary_packet_size,
|
||||
secondary_packet_size: value.debug.traffic.secondary_packet_size,
|
||||
packet_type: value.debug.traffic.packet_type,
|
||||
..Default::default()
|
||||
},
|
||||
cover_traffic: CoverTraffic {
|
||||
loop_cover_traffic_average_delay: value
|
||||
@@ -182,7 +181,6 @@ impl From<ConfigV5> for Config {
|
||||
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
|
||||
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
|
||||
},
|
||||
stats_reporting: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,13 +115,8 @@ where
|
||||
hardcoded_topology.get_gateways()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::current_gateways(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
)
|
||||
.await?
|
||||
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
|
||||
.await?
|
||||
};
|
||||
|
||||
// since we're registering with a brand new gateway,
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::{
|
||||
use log::info;
|
||||
use nym_client_core_gateways_storage::GatewayDetails;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
use nym_validator_client::UserAgent;
|
||||
use rand::rngs::OsRng;
|
||||
@@ -89,10 +88,6 @@ pub struct CommonClientInitArgs {
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub no_cover: bool,
|
||||
|
||||
/// Sets the address to report statistics
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub stats_reporting_address: Option<Recipient>,
|
||||
}
|
||||
|
||||
pub struct InitResultsWithConfig<T> {
|
||||
@@ -170,13 +165,8 @@ where
|
||||
hardcoded_topology.get_gateways()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::current_gateways(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
)
|
||||
.await?
|
||||
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
|
||||
.await?
|
||||
};
|
||||
|
||||
let gateway_setup = GatewaySetup::New {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg_attr(feature = "cli", derive(clap::Args))]
|
||||
@@ -57,8 +56,4 @@ pub struct CommonClientRunArgs {
|
||||
// has defined the conflict on that field itself
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub no_cover: bool,
|
||||
|
||||
/// Sets the address to report statistics
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub stats_reporting_address: Option<Recipient>,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::packet_statistics_control::PacketStatisticsReporter;
|
||||
use super::received_buffer::ReceivedBufferMessage;
|
||||
use super::statistics_control::StatisticsControl;
|
||||
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
|
||||
use crate::client::base_client::storage::helpers::store_client_keys;
|
||||
use crate::client::base_client::storage::MixnetClientStorage;
|
||||
@@ -12,6 +12,7 @@ use crate::client::key_manager::persistence::KeyStore;
|
||||
use crate::client::key_manager::ClientKeys;
|
||||
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
|
||||
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
|
||||
use crate::client::packet_statistics_control::PacketStatisticsControl;
|
||||
use crate::client::real_messages_control;
|
||||
use crate::client::real_messages_control::RealMessagesController;
|
||||
use crate::client::received_buffer::{
|
||||
@@ -48,8 +49,6 @@ use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::addressing::nodes::NodeIdentity;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
|
||||
use nym_statistics_common::clients::ClientStatsSender;
|
||||
use nym_statistics_common::generate_client_stats_id;
|
||||
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
|
||||
use nym_task::{TaskClient, TaskHandle};
|
||||
use nym_topology::provider_trait::TopologyProvider;
|
||||
@@ -60,7 +59,6 @@ use std::fmt::Debug;
|
||||
use std::os::raw::c_int as RawFd;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(all(
|
||||
@@ -275,7 +273,7 @@ where
|
||||
self_address: Recipient,
|
||||
topology_accessor: TopologyAccessor,
|
||||
mix_tx: BatchMixMessageSender,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
shutdown: TaskClient,
|
||||
) {
|
||||
info!("Starting loop cover traffic stream...");
|
||||
@@ -308,7 +306,7 @@ where
|
||||
client_connection_rx: ConnectionCommandReceiver,
|
||||
shutdown: TaskClient,
|
||||
packet_type: PacketType,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) {
|
||||
info!("Starting real traffic stream...");
|
||||
|
||||
@@ -337,7 +335,7 @@ where
|
||||
reply_key_storage: SentReplyKeys,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
shutdown: TaskClient,
|
||||
metrics_reporter: ClientStatsSender,
|
||||
packet_statistics_control: PacketStatisticsReporter,
|
||||
) {
|
||||
info!("Starting received messages buffer controller...");
|
||||
let controller: ReceivedMessagesBufferController<SphinxMessageReceiver> =
|
||||
@@ -347,7 +345,7 @@ where
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
metrics_reporter,
|
||||
packet_statistics_control,
|
||||
);
|
||||
controller.start_with_shutdown(shutdown)
|
||||
}
|
||||
@@ -358,7 +356,6 @@ where
|
||||
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
|
||||
details_store: &S::GatewaysDetailsStore,
|
||||
packet_router: PacketRouter,
|
||||
stats_reporter: ClientStatsSender,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
|
||||
where
|
||||
@@ -374,12 +371,7 @@ where
|
||||
|
||||
let mut gateway_client =
|
||||
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client {
|
||||
existing_client.upgrade(
|
||||
packet_router,
|
||||
bandwidth_controller,
|
||||
stats_reporter,
|
||||
shutdown,
|
||||
)
|
||||
existing_client.upgrade(packet_router, bandwidth_controller, shutdown)
|
||||
} else {
|
||||
let cfg = GatewayConfig::new(
|
||||
details.gateway_id,
|
||||
@@ -400,7 +392,6 @@ where
|
||||
Some(details.shared_key),
|
||||
packet_router,
|
||||
bandwidth_controller,
|
||||
stats_reporter,
|
||||
shutdown,
|
||||
)
|
||||
};
|
||||
@@ -453,7 +444,6 @@ where
|
||||
Ok(gateway_client)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn setup_gateway_transceiver(
|
||||
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
|
||||
config: &Config,
|
||||
@@ -461,7 +451,6 @@ where
|
||||
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
|
||||
details_store: &S::GatewaysDetailsStore,
|
||||
packet_router: PacketRouter,
|
||||
stats_reporter: ClientStatsSender,
|
||||
mut shutdown: TaskClient,
|
||||
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
|
||||
where
|
||||
@@ -492,7 +481,6 @@ where
|
||||
bandwidth_controller,
|
||||
details_store,
|
||||
packet_router,
|
||||
stats_reporter,
|
||||
shutdown,
|
||||
)
|
||||
.await?;
|
||||
@@ -514,10 +502,15 @@ where
|
||||
min_gateway_performance: config_topology.minimum_gateway_performance,
|
||||
},
|
||||
nym_api_urls,
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
user_agent,
|
||||
)),
|
||||
config::TopologyStructure::GeoAware(group_by) => {
|
||||
Box::new(GeoAwareTopologyProvider::new(nym_api_urls, group_by))
|
||||
Box::new(GeoAwareTopologyProvider::new(
|
||||
nym_api_urls,
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
group_by,
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -593,23 +586,11 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_statistics_control(
|
||||
config: &Config,
|
||||
user_agent: Option<UserAgent>,
|
||||
client_stats_id: String,
|
||||
input_sender: Sender<InputMessage>,
|
||||
shutdown: TaskClient,
|
||||
) -> ClientStatsSender {
|
||||
info!("Starting statistics control...");
|
||||
StatisticsControl::create_and_start_with_shutdown(
|
||||
config.debug.stats_reporting,
|
||||
user_agent
|
||||
.map(|u| u.application)
|
||||
.unwrap_or("unknown".to_string()),
|
||||
client_stats_id,
|
||||
input_sender.clone(),
|
||||
shutdown.with_suffix("controller"),
|
||||
)
|
||||
fn start_packet_statistics_control(shutdown: TaskClient) -> PacketStatisticsReporter {
|
||||
info!("Starting packet statistics control...");
|
||||
let (packet_statistics_control, packet_stats_reporter) = PacketStatisticsControl::new();
|
||||
packet_statistics_control.start_with_shutdown(shutdown);
|
||||
packet_stats_reporter
|
||||
}
|
||||
|
||||
fn start_mix_traffic_controller(
|
||||
@@ -739,14 +720,6 @@ where
|
||||
self.user_agent.clone(),
|
||||
);
|
||||
|
||||
let stats_reporter = Self::start_statistics_control(
|
||||
self.config,
|
||||
self.user_agent.clone(),
|
||||
generate_client_stats_id(*self_address.identity()),
|
||||
input_sender.clone(),
|
||||
shutdown.fork("statistics_control"),
|
||||
);
|
||||
|
||||
// needs to be started as the first thing to block if required waiting for the gateway
|
||||
Self::start_topology_refresher(
|
||||
topology_provider,
|
||||
@@ -758,6 +731,9 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
let packet_stats_reporter =
|
||||
Self::start_packet_statistics_control(shutdown.fork("packet_statistics_control"));
|
||||
|
||||
let gateway_packet_router = PacketRouter::new(
|
||||
ack_sender,
|
||||
mixnet_messages_sender,
|
||||
@@ -771,7 +747,6 @@ where
|
||||
bandwidth_controller,
|
||||
&details_store,
|
||||
gateway_packet_router,
|
||||
stats_reporter.clone(),
|
||||
shutdown.fork("gateway_transceiver"),
|
||||
)
|
||||
.await?;
|
||||
@@ -790,7 +765,7 @@ where
|
||||
reply_storage.key_storage(),
|
||||
reply_controller_sender.clone(),
|
||||
shutdown.fork("received_messages_buffer"),
|
||||
stats_reporter.clone(),
|
||||
packet_stats_reporter.clone(),
|
||||
);
|
||||
|
||||
// The message_sender is the transmitter for any component generating sphinx packets
|
||||
@@ -829,7 +804,7 @@ where
|
||||
client_connection_rx,
|
||||
shutdown.fork("real_traffic_controller"),
|
||||
self.config.debug.traffic.packet_type,
|
||||
stats_reporter.clone(),
|
||||
packet_stats_reporter.clone(),
|
||||
);
|
||||
|
||||
if !self
|
||||
@@ -844,7 +819,7 @@ where
|
||||
self_address,
|
||||
shared_topology_accessor.clone(),
|
||||
message_sender,
|
||||
stats_reporter.clone(),
|
||||
packet_stats_reporter,
|
||||
shutdown.fork("cover_traffic_stream"),
|
||||
);
|
||||
}
|
||||
@@ -872,7 +847,6 @@ where
|
||||
topology_accessor: shared_topology_accessor,
|
||||
gateway_connection: GatewayConnection { gateway_ws_fd },
|
||||
},
|
||||
stats_reporter,
|
||||
task_handle: shutdown,
|
||||
})
|
||||
}
|
||||
@@ -884,7 +858,6 @@ pub struct BaseClient {
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
pub stats_reporter: ClientStatsSender,
|
||||
|
||||
pub task_handle: TaskHandle,
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::mix_traffic::BatchMixMessageSender;
|
||||
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
|
||||
use crate::client::topology_control::TopologyAccessor;
|
||||
use crate::{config, spawn_future};
|
||||
use futures::task::{Context, Poll};
|
||||
@@ -12,7 +13,6 @@ use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::cover::generate_loop_cover_packet;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use nym_sphinx::utils::sample_poisson_duration;
|
||||
use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender};
|
||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
@@ -63,7 +63,7 @@ where
|
||||
|
||||
packet_type: PacketType,
|
||||
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
impl<R> Stream for LoopCoverTrafficStream<R>
|
||||
@@ -109,7 +109,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
topology_access: TopologyAccessor,
|
||||
traffic_config: config::Traffic,
|
||||
cover_config: config::CoverTraffic,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let rng = OsRng;
|
||||
|
||||
@@ -198,9 +198,9 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.stats_tx.report(
|
||||
PacketStatisticsEvent::CoverPacketSent(cover_traffic_packet_size.size()).into(),
|
||||
);
|
||||
self.stats_tx.report(PacketStatisticsEvent::CoverPacketSent(
|
||||
cover_traffic_packet_size.size(),
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: I'm not entirely sure whether this is really required, because I'm not 100%
|
||||
|
||||
@@ -14,7 +14,7 @@ use std::os::raw::c_int as RawFd;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use futures::channel::oneshot;
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
|
||||
// we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch
|
||||
#[derive(Debug, Error)]
|
||||
@@ -170,7 +170,7 @@ pub struct LocalGateway {
|
||||
|
||||
// 'sender' part
|
||||
/// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers.
|
||||
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender,
|
||||
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
|
||||
|
||||
// 'receiver' part
|
||||
packet_router_tx: Option<oneshot::Sender<PacketRouter>>,
|
||||
@@ -180,7 +180,7 @@ pub struct LocalGateway {
|
||||
impl LocalGateway {
|
||||
pub fn new(
|
||||
local_identity: identity::PublicKey,
|
||||
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender,
|
||||
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
|
||||
packet_router_tx: oneshot::Sender<PacketRouter>,
|
||||
) -> Self {
|
||||
LocalGateway {
|
||||
@@ -208,7 +208,8 @@ mod nonwasm_sealed {
|
||||
impl GatewaySender for LocalGateway {
|
||||
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
|
||||
self.packet_forwarder
|
||||
.forward_packet(packet)
|
||||
.unbounded_send(packet)
|
||||
.map_err(|err| err.into_send_error())
|
||||
.map_err(erase_err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ pub(crate) mod helpers;
|
||||
pub mod inbound_messages;
|
||||
pub mod key_manager;
|
||||
pub mod mix_traffic;
|
||||
pub(crate) mod packet_statistics_control;
|
||||
pub mod real_messages_control;
|
||||
pub mod received_buffer;
|
||||
pub mod replies;
|
||||
pub mod statistics_control;
|
||||
pub mod topology_control;
|
||||
pub(crate) mod transmission_buffer;
|
||||
|
||||
+213
-107
@@ -1,25 +1,48 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::ClientStatsEvents;
|
||||
use core::fmt;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use nym_metrics::{inc, inc_by};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use si_scale::helpers::bibytes2;
|
||||
|
||||
// Metrics server
|
||||
use futures::future::{FusedFuture, OptionFuture};
|
||||
use futures::FutureExt;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use http_body_util::Full;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use hyper::body::Bytes;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use hyper::server::conn::http1;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use hyper::service::service_fn;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use hyper::{Request, Response};
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use hyper_util::rt::TokioIo;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use std::convert::Infallible;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(feature = "metrics-server")]
|
||||
use std::net::SocketAddr;
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use crate::spawn_future;
|
||||
|
||||
// Time interval between reporting packet statistics
|
||||
const PACKET_REPORT_INTERVAL_SECS: u64 = 2;
|
||||
// Interval for taking snapshots of the packet statistics
|
||||
const SNAPSHOT_INTERVAL_MS: u64 = 500;
|
||||
// When computing rates, we include snapshots that are up to this old. We set it to some odd number
|
||||
// a tad larger than an integer number of snapshot intervals, so that we don't have to worry about
|
||||
// threshold effects.
|
||||
// Also, set it larger than the packet report interval so that we don't miss notable singular events
|
||||
const RECORDING_WINDOW_MS: u64 = 2300;
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) struct PacketStatistics {
|
||||
#[derive(Default, Debug, Clone)]
|
||||
struct PacketStatistics {
|
||||
// Sent
|
||||
real_packets_sent: u64,
|
||||
real_packets_sent_size: usize,
|
||||
@@ -49,7 +72,7 @@ pub(crate) struct PacketStatistics {
|
||||
}
|
||||
|
||||
impl PacketStatistics {
|
||||
fn handle(&mut self, event: PacketStatisticsEvent) {
|
||||
fn handle_event(&mut self, event: PacketStatisticsEvent) {
|
||||
match event {
|
||||
PacketStatisticsEvent::RealPacketSent(packet_size) => {
|
||||
self.real_packets_sent += 1;
|
||||
@@ -166,64 +189,29 @@ impl std::ops::Sub for PacketStatistics {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MixnetBandwidthStatisticsEvent {
|
||||
pub rates: PacketRates,
|
||||
}
|
||||
|
||||
impl MixnetBandwidthStatisticsEvent {
|
||||
pub fn new(rates: PacketRates) -> Self {
|
||||
Self { rates }
|
||||
}
|
||||
}
|
||||
|
||||
impl nym_task::TaskStatusEvent for MixnetBandwidthStatisticsEvent {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MixnetBandwidthStatisticsEvent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.rates.summary())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PacketRates {
|
||||
pub real_packets_sent: f64,
|
||||
pub real_packets_sent_size: f64,
|
||||
pub cover_packets_sent: f64,
|
||||
pub cover_packets_sent_size: f64,
|
||||
struct PacketRates {
|
||||
real_packets_sent: f64,
|
||||
real_packets_sent_size: f64,
|
||||
cover_packets_sent: f64,
|
||||
cover_packets_sent_size: f64,
|
||||
|
||||
pub real_packets_received: f64,
|
||||
pub real_packets_received_size: f64,
|
||||
pub cover_packets_received: f64,
|
||||
pub cover_packets_received_size: f64,
|
||||
real_packets_received: f64,
|
||||
real_packets_received_size: f64,
|
||||
cover_packets_received: f64,
|
||||
cover_packets_received_size: f64,
|
||||
|
||||
pub total_acks_received: f64,
|
||||
pub total_acks_received_size: f64,
|
||||
pub real_acks_received: f64,
|
||||
pub real_acks_received_size: f64,
|
||||
pub cover_acks_received: f64,
|
||||
pub cover_acks_received_size: f64,
|
||||
total_acks_received: f64,
|
||||
total_acks_received_size: f64,
|
||||
real_acks_received: f64,
|
||||
real_acks_received_size: f64,
|
||||
cover_acks_received: f64,
|
||||
cover_acks_received_size: f64,
|
||||
|
||||
pub real_packets_queued: f64,
|
||||
pub retransmissions_queued: f64,
|
||||
pub reply_surbs_queued: f64,
|
||||
pub additional_reply_surbs_queued: f64,
|
||||
}
|
||||
|
||||
impl fmt::Display for PacketRates {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"down: {}/s, up: {}/s (cover down: {}/s, cover up: {}/s)",
|
||||
bibytes2(self.real_packets_received_size),
|
||||
bibytes2(self.real_packets_sent_size),
|
||||
bibytes2(self.cover_packets_received_size),
|
||||
bibytes2(self.cover_packets_sent_size),
|
||||
)
|
||||
}
|
||||
real_packets_queued: f64,
|
||||
retransmissions_queued: f64,
|
||||
reply_surbs_queued: f64,
|
||||
additional_reply_surbs_queued: f64,
|
||||
}
|
||||
|
||||
impl From<PacketStatistics> for PacketRates {
|
||||
@@ -342,46 +330,56 @@ impl PacketRates {
|
||||
}
|
||||
}
|
||||
|
||||
/// Event Space used for counting the Packet types used in a connection.
|
||||
#[derive(Debug)]
|
||||
pub enum PacketStatisticsEvent {
|
||||
/// The real packets sent. Recall that acks are sent by the gateway, so it's not included here.
|
||||
pub(crate) enum PacketStatisticsEvent {
|
||||
// The real packets sent. Recall that acks are sent by the gateway, so it's not included here.
|
||||
RealPacketSent(usize),
|
||||
/// The cover packets sent
|
||||
// The cover packets sent
|
||||
CoverPacketSent(usize),
|
||||
|
||||
/// Real packets received
|
||||
// Real packets received
|
||||
RealPacketReceived(usize),
|
||||
/// Cover packets received
|
||||
// Cover packets received
|
||||
CoverPacketReceived(usize),
|
||||
|
||||
/// Ack of any type received. This is mostly used as a consistency check, and should be the sum
|
||||
/// of real and cover acks received.
|
||||
// Ack of any type received. This is mostly used as a consistency check, and should be the sum
|
||||
// of real and cover acks received.
|
||||
AckReceived(usize),
|
||||
/// Out of the total acks received, this is the subset of those that were real
|
||||
// Out of the total acks received, this is the subset of those that were real
|
||||
RealAckReceived(usize),
|
||||
/// Out of the total acks received, this is the subset of those that were for cover traffic
|
||||
// Out of the total acks received, this is the subset of those that were for cover traffic
|
||||
CoverAckReceived(usize),
|
||||
|
||||
/// Types of packets queued
|
||||
// Types of packets queued
|
||||
RealPacketQueued,
|
||||
/// Types of packets queued
|
||||
RetransmissionQueued,
|
||||
/// Types of packets queued
|
||||
ReplySurbRequestQueued,
|
||||
/// Types of packets queued
|
||||
AdditionalReplySurbRequestQueued,
|
||||
}
|
||||
|
||||
impl From<PacketStatisticsEvent> for ClientStatsEvents {
|
||||
fn from(event: PacketStatisticsEvent) -> ClientStatsEvents {
|
||||
ClientStatsEvents::PacketStatistics(event)
|
||||
type PacketStatisticsReceiver = tokio::sync::mpsc::UnboundedReceiver<PacketStatisticsEvent>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct PacketStatisticsReporter {
|
||||
stats_tx: tokio::sync::mpsc::UnboundedSender<PacketStatisticsEvent>,
|
||||
}
|
||||
|
||||
impl PacketStatisticsReporter {
|
||||
pub(crate) fn new(stats_tx: tokio::sync::mpsc::UnboundedSender<PacketStatisticsEvent>) -> Self {
|
||||
Self { stats_tx }
|
||||
}
|
||||
|
||||
pub(crate) fn report(&self, event: PacketStatisticsEvent) {
|
||||
self.stats_tx.send(event).unwrap_or_else(|err| {
|
||||
log::error!("Failed to report packet stat: {:?}", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Statistics tracking for Packet based I/O
|
||||
#[derive(Default)]
|
||||
pub struct PacketStatisticsControl {
|
||||
pub(crate) struct PacketStatisticsControl {
|
||||
// Incoming packet stats events from other tasks
|
||||
stats_rx: PacketStatisticsReceiver,
|
||||
|
||||
// Keep track of packet statistics over time
|
||||
stats: PacketStatistics,
|
||||
|
||||
@@ -394,28 +392,18 @@ pub struct PacketStatisticsControl {
|
||||
}
|
||||
|
||||
impl PacketStatisticsControl {
|
||||
pub(crate) fn handle_event(&mut self, event: PacketStatisticsEvent) {
|
||||
self.stats.handle(event)
|
||||
}
|
||||
pub(crate) fn new() -> (Self, PacketStatisticsReporter) {
|
||||
let (stats_tx, stats_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
pub(crate) fn snapshot(&mut self) {
|
||||
self.update_history();
|
||||
self.update_rates();
|
||||
}
|
||||
|
||||
pub(crate) fn report(&self) -> PacketStatistics {
|
||||
self.stats.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn local_report(&mut self, task_client: &mut nym_task::TaskClient) {
|
||||
let rates = self.report_rates();
|
||||
self.check_for_notable_events();
|
||||
self.report_counters();
|
||||
|
||||
// Report our current bandwidth used to e.g a GUI client
|
||||
if let Some(rates) = rates {
|
||||
task_client.send_status_msg(Box::new(MixnetBandwidthStatisticsEvent::new(rates)));
|
||||
}
|
||||
(
|
||||
Self {
|
||||
stats_rx,
|
||||
stats: PacketStatistics::default(),
|
||||
history: VecDeque::new(),
|
||||
rates: VecDeque::new(),
|
||||
},
|
||||
PacketStatisticsReporter::new(stats_tx),
|
||||
)
|
||||
}
|
||||
|
||||
// Add the current stats to the history, and remove old ones.
|
||||
@@ -468,13 +456,11 @@ impl PacketStatisticsControl {
|
||||
}
|
||||
}
|
||||
|
||||
fn report_rates(&self) -> Option<PacketRates> {
|
||||
fn report_rates(&self) {
|
||||
if let Some((_, rates)) = self.rates.back() {
|
||||
log::debug!("{}", rates.summary());
|
||||
log::debug!("{}", rates.detailed_summary());
|
||||
return Some(rates.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn report_counters(&self) {
|
||||
@@ -512,4 +498,124 @@ impl PacketStatisticsControl {
|
||||
|
||||
// IDEA: if there is a burst of acks, that could indicate tokio task starvation.
|
||||
}
|
||||
|
||||
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
|
||||
log::debug!("Started PacketStatisticsControl with graceful shutdown support");
|
||||
|
||||
let report_interval = Duration::from_secs(PACKET_REPORT_INTERVAL_SECS);
|
||||
let mut report_interval = tokio::time::interval(report_interval);
|
||||
let snapshot_interval = Duration::from_millis(SNAPSHOT_INTERVAL_MS);
|
||||
let mut snapshot_interval = tokio::time::interval(snapshot_interval);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
|
||||
log::warn!("Metrics server is not supported on wasm32-unknown-unknown");
|
||||
let listener: Option<WasmEmpty> = None;
|
||||
} else if #[cfg(feature = "metrics-server")] {
|
||||
let mut metrics_port = 18000;
|
||||
let listener: Option<TcpListener>;
|
||||
loop {
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], metrics_port));
|
||||
match TcpListener::bind(addr).await {
|
||||
Ok(l) => {
|
||||
log::info!("###############################");
|
||||
log::info!("Metrics endpoint is at: {:?}", l.local_addr());
|
||||
log::info!("###############################");
|
||||
listener = Some(l);
|
||||
break;
|
||||
},
|
||||
Err(err) => {
|
||||
log::warn!("Failed to bind metrics server: {:?}", err);
|
||||
metrics_port += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
log::info!("Metrics server is disabled!");
|
||||
let listener: Option<TcpListener> = None;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
// it seems at some point tokio changed its select precondition evaluation,
|
||||
// and it's no longer checked before the future is evaluated.
|
||||
let accept_future: OptionFuture<_> = listener
|
||||
.as_ref()
|
||||
.map(|l| l.accept())
|
||||
.map(FutureExt::fuse)
|
||||
.into();
|
||||
|
||||
tokio::select! {
|
||||
stats_event = self.stats_rx.recv() => match stats_event {
|
||||
Some(stats_event) => {
|
||||
log::trace!("PacketStatisticsControl: Received stats event");
|
||||
self.stats.handle_event(stats_event);
|
||||
},
|
||||
None => {
|
||||
log::trace!("PacketStatisticsControl: stopping since stats channel was closed");
|
||||
break;
|
||||
}
|
||||
},
|
||||
// conditional will disable the branch if we're in wasm32-unknown-unknown
|
||||
// use `_` to calm down clippy when running for wasm
|
||||
_result = accept_future, if !accept_future.is_terminated() => {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
|
||||
if let Some(Ok((stream, _))) = _result {
|
||||
let io = TokioIo::new(stream);
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
if let Err(err) = http1::Builder::new()
|
||||
.serve_connection(io, service_fn(serve_metrics))
|
||||
.await
|
||||
{
|
||||
log::warn!("Error serving connection: {:?}", err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log::warn!("Error accepting connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = snapshot_interval.tick() => {
|
||||
self.update_history();
|
||||
self.update_rates();
|
||||
}
|
||||
_ = report_interval.tick() => {
|
||||
self.report_rates();
|
||||
self.check_for_notable_events();
|
||||
self.report_counters();
|
||||
}
|
||||
_ = shutdown.recv_with_delay() => {
|
||||
log::trace!("PacketStatisticsControl: Received shutdown");
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
log::debug!("PacketStatisticsControl: Exiting");
|
||||
}
|
||||
|
||||
pub(crate) fn start_with_shutdown(mut self, task_client: nym_task::TaskClient) {
|
||||
spawn_future(async move {
|
||||
self.run_with_shutdown(task_client).await;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
async fn serve_metrics(
|
||||
_: Request<hyper::body::Incoming>,
|
||||
) -> Result<Response<Full<Bytes>>, Infallible> {
|
||||
use nym_metrics::metrics;
|
||||
|
||||
Ok(Response::new(Full::new(Bytes::from(metrics!()))))
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
struct WasmEmpty;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
impl WasmEmpty {
|
||||
async fn accept(&self) {}
|
||||
}
|
||||
+7
-7
@@ -1,9 +1,9 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::action_controller::{AckActionSender, Action};
|
||||
use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender};
|
||||
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
|
||||
|
||||
use super::action_controller::{AckActionSender, Action};
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_gateway_client::AcknowledgementReceiver;
|
||||
@@ -19,7 +19,7 @@ pub(super) struct AcknowledgementListener {
|
||||
ack_key: Arc<AckKey>,
|
||||
ack_receiver: AcknowledgementReceiver,
|
||||
action_sender: AckActionSender,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
impl AcknowledgementListener {
|
||||
@@ -27,7 +27,7 @@ impl AcknowledgementListener {
|
||||
ack_key: Arc<AckKey>,
|
||||
ack_receiver: AcknowledgementReceiver,
|
||||
action_sender: AckActionSender,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
AcknowledgementListener {
|
||||
ack_key,
|
||||
@@ -40,7 +40,7 @@ impl AcknowledgementListener {
|
||||
async fn on_ack(&mut self, ack_content: Vec<u8>) {
|
||||
trace!("Received an ack");
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::AckReceived(ack_content.len()).into());
|
||||
.report(PacketStatisticsEvent::AckReceived(ack_content.len()));
|
||||
|
||||
let frag_id = match recover_identifier(&self.ack_key, &ack_content)
|
||||
.map(FragmentIdentifier::try_from_bytes)
|
||||
@@ -57,13 +57,13 @@ impl AcknowledgementListener {
|
||||
if frag_id == COVER_FRAG_ID {
|
||||
trace!("Received an ack for a cover message - no need to do anything");
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::CoverAckReceived(ack_content.len()).into());
|
||||
.report(PacketStatisticsEvent::CoverAckReceived(ack_content.len()));
|
||||
return;
|
||||
}
|
||||
|
||||
trace!("Received {} from the mix network", frag_id);
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into());
|
||||
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()));
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_remove(frag_id))
|
||||
.unwrap();
|
||||
|
||||
+9
-12
@@ -30,8 +30,7 @@ pub(crate) enum Action {
|
||||
InsertPending(Vec<PendingAcknowledgement>),
|
||||
|
||||
/// Removes given `PendingAcknowledgement` from the 'shared' state. Also cancels the retransmission timer.
|
||||
/// Initiated by `AcknowledgementListener` upon receiving the acknowledgement. Also by `RetransmissionRequestListener`
|
||||
/// upon deciding to abandon the data.
|
||||
/// Initiated by `AcknowledgementListener`
|
||||
RemovePending(FragmentIdentifier),
|
||||
|
||||
/// Starts the retransmission timer on given `PendingAcknowledgement` with the `Duration` based on
|
||||
@@ -42,7 +41,7 @@ pub(crate) enum Action {
|
||||
|
||||
/// Updates the expected delay of given `PendingAcknowledgement` with the new provided `SphinxDelay`.
|
||||
/// Initiated by `RetransmissionRequestListener`
|
||||
UpdatePendingAck(FragmentIdentifier, SphinxDelay),
|
||||
UpdateDelay(FragmentIdentifier, SphinxDelay),
|
||||
}
|
||||
|
||||
impl Action {
|
||||
@@ -58,8 +57,8 @@ impl Action {
|
||||
Action::StartTimer(frag_id)
|
||||
}
|
||||
|
||||
pub(crate) fn new_update_pending_ack(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
|
||||
Action::UpdatePendingAck(frag_id, delay)
|
||||
pub(crate) fn new_update_delay(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
|
||||
Action::UpdateDelay(frag_id, delay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +135,7 @@ impl ActionController {
|
||||
}
|
||||
|
||||
fn handle_start_timer(&mut self, frag_id: FragmentIdentifier) {
|
||||
trace!("{frag_id} is starting its timer");
|
||||
trace!("{} is starting its timer", frag_id);
|
||||
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
|
||||
// the fact that this branch is now POSSIBLE is a sign of a need to refactor this whole
|
||||
@@ -194,7 +193,7 @@ impl ActionController {
|
||||
|
||||
// initiated basically as a first step of retransmission. At first data has its delay updated
|
||||
// (as new sphinx packet was created with new expected delivery time)
|
||||
fn handle_update_pending_ack(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
|
||||
fn handle_update_delay(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
|
||||
trace!("{} is updating its delay", frag_id);
|
||||
// TODO: is it possible to solve this without either locking or temporarily removing the value?
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
|
||||
@@ -203,7 +202,7 @@ impl ActionController {
|
||||
// reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference
|
||||
// was dropped hence this unwrap is safe.
|
||||
let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap();
|
||||
inner_data.update_retransmitted(delay);
|
||||
inner_data.update_delay(delay);
|
||||
|
||||
self.pending_acks_data
|
||||
.insert(frag_id, (Arc::new(inner_data), queue_key));
|
||||
@@ -226,7 +225,7 @@ impl ActionController {
|
||||
// about it. Perhaps just reschedule it at later point?
|
||||
let frag_id = expired_ack.into_inner();
|
||||
|
||||
trace!("{frag_id} has expired");
|
||||
trace!("{} has expired", frag_id);
|
||||
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
|
||||
if queue_key.is_none() {
|
||||
@@ -259,9 +258,7 @@ impl ActionController {
|
||||
Action::InsertPending(pending_acks) => self.handle_insert(pending_acks),
|
||||
Action::RemovePending(frag_id) => self.handle_remove(frag_id),
|
||||
Action::StartTimer(frag_id) => self.handle_start_timer(frag_id),
|
||||
Action::UpdatePendingAck(frag_id, delay) => {
|
||||
self.handle_update_pending_ack(frag_id, delay)
|
||||
}
|
||||
Action::UpdateDelay(frag_id, delay) => self.handle_update_delay(frag_id, delay),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use self::{
|
||||
sent_notification_listener::SentNotificationListener,
|
||||
};
|
||||
use crate::client::inbound_messages::InputMessageReceiver;
|
||||
use crate::client::packet_statistics_control::PacketStatisticsReporter;
|
||||
use crate::client::real_messages_control::message_handler::MessageHandler;
|
||||
use crate::client::replies::reply_controller::ReplyControllerSender;
|
||||
use crate::spawn_future;
|
||||
@@ -23,7 +24,6 @@ use nym_sphinx::{
|
||||
chunking::fragment::{Fragment, FragmentIdentifier},
|
||||
Delay as SphinxDelay,
|
||||
};
|
||||
use nym_statistics_common::clients::ClientStatsSender;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::{
|
||||
sync::{Arc, Weak},
|
||||
@@ -71,7 +71,6 @@ pub(crate) struct PendingAcknowledgement {
|
||||
delay: SphinxDelay,
|
||||
destination: PacketDestination,
|
||||
mix_hops: Option<u8>,
|
||||
retransmissions: u32,
|
||||
}
|
||||
|
||||
impl PendingAcknowledgement {
|
||||
@@ -87,7 +86,6 @@ impl PendingAcknowledgement {
|
||||
delay,
|
||||
destination: PacketDestination::KnownRecipient(recipient.into()),
|
||||
mix_hops,
|
||||
retransmissions: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +105,6 @@ impl PendingAcknowledgement {
|
||||
// Messages sent using SURBs are using the number of mix hops set by the recipient when
|
||||
// they provided the SURBs, so it doesn't make sense to include it here.
|
||||
mix_hops: None,
|
||||
retransmissions: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,9 +116,8 @@ impl PendingAcknowledgement {
|
||||
self.message_chunk.clone()
|
||||
}
|
||||
|
||||
fn update_retransmitted(&mut self, new_delay: SphinxDelay) {
|
||||
fn update_delay(&mut self, new_delay: SphinxDelay) {
|
||||
self.delay = new_delay;
|
||||
self.retransmissions += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,9 +163,6 @@ impl AcknowledgementControllerConnectors {
|
||||
|
||||
/// Configurable parameters of the `AcknowledgementController`
|
||||
pub(super) struct Config {
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
maximum_retransmissions: Option<u32>,
|
||||
|
||||
/// Given ack timeout in the form a * BASE_DELAY + b, it specifies the additive part `b`
|
||||
ack_wait_addition: Duration,
|
||||
|
||||
@@ -181,13 +174,8 @@ pub(super) struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub(super) fn new(
|
||||
maximum_retransmissions: Option<u32>,
|
||||
ack_wait_addition: Duration,
|
||||
ack_wait_multiplier: f64,
|
||||
) -> Self {
|
||||
pub(super) fn new(ack_wait_addition: Duration, ack_wait_multiplier: f64) -> Self {
|
||||
Config {
|
||||
maximum_retransmissions,
|
||||
ack_wait_addition,
|
||||
ack_wait_multiplier,
|
||||
packet_size: Default::default(),
|
||||
@@ -221,7 +209,7 @@ where
|
||||
connectors: AcknowledgementControllerConnectors,
|
||||
message_handler: MessageHandler<R>,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let (retransmission_tx, retransmission_rx) = mpsc::unbounded();
|
||||
|
||||
@@ -250,7 +238,6 @@ where
|
||||
|
||||
// will listen for any ack timeouts and trigger retransmission
|
||||
let retransmission_request_listener = RetransmissionRequestListener::new(
|
||||
config.maximum_retransmissions,
|
||||
connectors.ack_action_sender.clone(),
|
||||
message_handler,
|
||||
retransmission_rx,
|
||||
|
||||
+3
-16
@@ -20,7 +20,6 @@ use std::sync::{Arc, Weak};
|
||||
|
||||
// responsible for packet retransmission upon fired timer
|
||||
pub(super) struct RetransmissionRequestListener<R> {
|
||||
maximum_retransmissions: Option<u32>,
|
||||
action_sender: AckActionSender,
|
||||
message_handler: MessageHandler<R>,
|
||||
request_receiver: RetransmissionRequestReceiver,
|
||||
@@ -32,14 +31,12 @@ where
|
||||
R: CryptoRng + Rng,
|
||||
{
|
||||
pub(super) fn new(
|
||||
maximum_retransmissions: Option<u32>,
|
||||
action_sender: AckActionSender,
|
||||
message_handler: MessageHandler<R>,
|
||||
request_receiver: RetransmissionRequestReceiver,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
) -> Self {
|
||||
RetransmissionRequestListener {
|
||||
maximum_retransmissions,
|
||||
action_sender,
|
||||
message_handler,
|
||||
request_receiver,
|
||||
@@ -80,18 +77,6 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
|
||||
|
||||
if let Some(limit) = self.maximum_retransmissions {
|
||||
if timed_out_ack.retransmissions >= limit {
|
||||
warn!("reached maximum number of allowed retransmissions for the packet");
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_remove(frag_id))
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_prepared_fragment = match &timed_out_ack.destination {
|
||||
PacketDestination::Anonymous {
|
||||
recipient_tag,
|
||||
@@ -116,6 +101,8 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
|
||||
|
||||
let prepared_fragment = match maybe_prepared_fragment {
|
||||
Ok(prepared_fragment) => prepared_fragment,
|
||||
Err(err) => {
|
||||
@@ -149,7 +136,7 @@ where
|
||||
// with the additional poisson delay.
|
||||
// And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer`
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_update_pending_ack(frag_id, new_delay))
|
||||
.unbounded_send(Action::new_update_delay(frag_id, new_delay))
|
||||
.unwrap();
|
||||
|
||||
// send to `OutQueueControl` to eventually send to the mix network
|
||||
|
||||
@@ -91,9 +91,6 @@ pub(crate) struct Config {
|
||||
/// and surb-based are going to be sent.
|
||||
sender_address: Recipient,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
deterministic_route_selection: bool,
|
||||
|
||||
/// Average delay a data packet is going to get delay at a single mixnode.
|
||||
average_packet_delay: Duration,
|
||||
|
||||
@@ -117,12 +114,10 @@ impl Config {
|
||||
sender_address: Recipient,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
deterministic_route_selection: bool,
|
||||
) -> Self {
|
||||
Config {
|
||||
ack_key,
|
||||
sender_address,
|
||||
deterministic_route_selection,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
||||
@@ -181,7 +176,6 @@ where
|
||||
{
|
||||
let message_preparer = MessagePreparer::new(
|
||||
rng,
|
||||
config.deterministic_route_selection,
|
||||
config.sender_address,
|
||||
config.average_packet_delay,
|
||||
config.average_ack_delay,
|
||||
@@ -640,7 +634,7 @@ where
|
||||
|
||||
pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) {
|
||||
self.action_sender
|
||||
.unbounded_send(Action::UpdatePendingAck(id, new_delay))
|
||||
.unbounded_send(Action::UpdateDelay(id, new_delay))
|
||||
.expect("action control task has died")
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::client::replies::reply_controller;
|
||||
use crate::config;
|
||||
pub(crate) use acknowledgement_control::{AckActionSender, Action};
|
||||
|
||||
use nym_statistics_common::clients::ClientStatsSender;
|
||||
use super::packet_statistics_control::PacketStatisticsReporter;
|
||||
|
||||
pub(crate) mod acknowledgement_control;
|
||||
pub(crate) mod message_handler;
|
||||
@@ -65,7 +65,6 @@ pub struct Config {
|
||||
impl<'a> From<&'a Config> for acknowledgement_control::Config {
|
||||
fn from(cfg: &'a Config) -> Self {
|
||||
acknowledgement_control::Config::new(
|
||||
cfg.traffic.maximum_number_of_retransmissions,
|
||||
cfg.acks.ack_wait_addition,
|
||||
cfg.acks.ack_wait_multiplier,
|
||||
)
|
||||
@@ -98,7 +97,6 @@ impl<'a> From<&'a Config> for message_handler::Config {
|
||||
cfg.self_recipient,
|
||||
cfg.traffic.average_packet_delay,
|
||||
cfg.acks.average_ack_delay,
|
||||
cfg.traffic.deterministic_route_selection,
|
||||
)
|
||||
.with_custom_primary_packet_size(cfg.traffic.primary_packet_size)
|
||||
.with_custom_secondary_packet_size(cfg.traffic.secondary_packet_size)
|
||||
@@ -147,7 +145,7 @@ impl RealMessagesController<OsRng> {
|
||||
reply_controller_receiver: ReplyControllerReceiver,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
client_connection_rx: ConnectionCommandReceiver,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let rng = OsRng;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use self::sending_delay_controller::SendingDelayController;
|
||||
use crate::client::mix_traffic::BatchMixMessageSender;
|
||||
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
|
||||
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
|
||||
use crate::client::topology_control::TopologyAccessor;
|
||||
use crate::client::transmission_buffer::TransmissionBuffer;
|
||||
@@ -18,7 +19,6 @@ use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_sphinx::params::PacketSize;
|
||||
use nym_sphinx::preparer::PreparedFragment;
|
||||
use nym_sphinx::utils::sample_poisson_duration;
|
||||
use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender};
|
||||
use nym_task::connections::{
|
||||
ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
|
||||
};
|
||||
@@ -115,8 +115,8 @@ where
|
||||
/// Report queue lengths so that upstream can backoff sending data, and keep connections open.
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
|
||||
/// Channel used for sending metrics events (specifically `PacketStatistics` events) to the metrics tracker.
|
||||
stats_tx: ClientStatsSender,
|
||||
/// Channel used for sending statistics events to `PacketStatisticsControl`.
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -175,7 +175,7 @@ where
|
||||
topology_access: TopologyAccessor,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
client_connection_rx: ConnectionCommandReceiver,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
OutQueueControl {
|
||||
config,
|
||||
@@ -277,7 +277,7 @@ where
|
||||
} else {
|
||||
PacketStatisticsEvent::CoverPacketSent(packet_size)
|
||||
};
|
||||
self.stats_tx.report(event.into());
|
||||
self.stats_tx.report(event);
|
||||
}
|
||||
|
||||
// notify ack controller about sending our message only after we actually managed to push it
|
||||
@@ -373,13 +373,13 @@ where
|
||||
TransmissionLane::Retransmission => Some(PacketStatisticsEvent::RetransmissionQueued),
|
||||
};
|
||||
if let Some(stat_event) = stat_event {
|
||||
self.stats_tx.report(stat_event.into());
|
||||
self.stats_tx.report(stat_event);
|
||||
}
|
||||
// To avoid comparing apples to oranges when presenting the fraction of packets that are
|
||||
// retransmissions, we also need to keep track to the total number of real messages queued,
|
||||
// even though we also track the actual number of messages sent later in the pipeline.
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RealPacketQueued.into());
|
||||
.report(PacketStatisticsEvent::RealPacketQueued);
|
||||
|
||||
Some(real_next)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::replies::{
|
||||
reply_controller::ReplyControllerSender, reply_storage::SentReplyKeys,
|
||||
use crate::client::{
|
||||
packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter},
|
||||
replies::{reply_controller::ReplyControllerSender, reply_storage::SentReplyKeys},
|
||||
};
|
||||
use crate::spawn_future;
|
||||
use futures::channel::mpsc;
|
||||
@@ -19,7 +20,6 @@ use nym_sphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SurbEnc
|
||||
use nym_sphinx::message::{NymMessage, PlainMessage};
|
||||
use nym_sphinx::params::ReplySurbKeyDigestAlgorithm;
|
||||
use nym_sphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
|
||||
use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -46,7 +46,7 @@ struct ReceivedMessagesBufferInner<R: MessageReceiver> {
|
||||
// and every now and then remove ids older than X
|
||||
recently_reconstructed: HashSet<i32>,
|
||||
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
@@ -61,12 +61,16 @@ impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
// received and sent packets due to the sphinx layers being removed by the exit gateway
|
||||
// before it reaches the mixnet client.
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::CoverPacketReceived(fragment_data_size).into());
|
||||
.report(PacketStatisticsEvent::CoverPacketReceived(
|
||||
fragment_data_size,
|
||||
));
|
||||
return None;
|
||||
}
|
||||
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RealPacketReceived(fragment_data_size).into());
|
||||
.report(PacketStatisticsEvent::RealPacketReceived(
|
||||
fragment_data_size,
|
||||
));
|
||||
|
||||
let fragment = match self.message_receiver.recover_fragment(fragment_data) {
|
||||
Err(err) => {
|
||||
@@ -159,7 +163,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
reply_key_storage: SentReplyKeys,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
stats_tx: ClientStatsSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
ReceivedMessagesBuffer {
|
||||
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
|
||||
@@ -500,13 +504,13 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
|
||||
mixnet_packet_receiver: MixnetMessageReceiver,
|
||||
reply_key_storage: SentReplyKeys,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
metrics_reporter: ClientStatsSender,
|
||||
packet_statistics_reporter: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let received_buffer = ReceivedMessagesBuffer::new(
|
||||
local_encryption_keypair,
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
metrics_reporter,
|
||||
packet_statistics_reporter,
|
||||
);
|
||||
|
||||
ReceivedMessagesBufferController {
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! # Statistics collection and reporting.
|
||||
//!
|
||||
//! Modular metrics collection and reporting system. submodules can be added to collect different types of metrics.
|
||||
//! On creation the Statistics controller will start a task that will listen for incoming stats events and
|
||||
//! multiplex them out to the appropriate metrics module based on type.
|
||||
//!
|
||||
//! Adding A new module you need to write a new module that implements the `StatsObj` trait and add it to
|
||||
//! the `stats` hashmap in the `StatisticsControl` struct during it's initialization in the `new` function in
|
||||
//! this file.
|
||||
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
#![warn(clippy::todo)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use nym_client_core_config_types::StatsReporting;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_statistics_common::clients::{
|
||||
ClientStatsController, ClientStatsReceiver, ClientStatsSender,
|
||||
};
|
||||
use nym_task::connections::TransmissionLane;
|
||||
|
||||
use crate::{
|
||||
client::inbound_messages::{InputMessage, InputMessageSender},
|
||||
spawn_future,
|
||||
};
|
||||
|
||||
/// Time interval between reporting statistics locally (logging/task_client)
|
||||
const LOCAL_REPORT_INTERVAL: Duration = Duration::from_secs(2);
|
||||
/// Interval for taking snapshots of the statistics
|
||||
const SNAPSHOT_INTERVAL: Duration = Duration::from_millis(500);
|
||||
|
||||
/// Launches and manages metrics collection and reporting.
|
||||
///
|
||||
/// This is designed to be generic to allow for multiple types of metrics to be collected and
|
||||
/// reported.
|
||||
pub(crate) struct StatisticsControl {
|
||||
/// Keep store the different types of metrics collectors
|
||||
stats: ClientStatsController,
|
||||
|
||||
/// Incoming packet stats events from other tasks
|
||||
stats_rx: ClientStatsReceiver,
|
||||
|
||||
/// Channel to send stats report through the mixnet
|
||||
report_tx: InputMessageSender,
|
||||
|
||||
/// Config for stats reporting (enabled, address, interval)
|
||||
reporting_config: StatsReporting,
|
||||
}
|
||||
|
||||
impl StatisticsControl {
|
||||
pub(crate) fn create(
|
||||
reporting_config: StatsReporting,
|
||||
client_type: String,
|
||||
client_stats_id: String,
|
||||
report_tx: InputMessageSender,
|
||||
) -> (Self, ClientStatsSender) {
|
||||
let (stats_tx, stats_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
let stats = ClientStatsController::new(client_stats_id, client_type);
|
||||
|
||||
(
|
||||
StatisticsControl {
|
||||
stats,
|
||||
stats_rx,
|
||||
report_tx,
|
||||
reporting_config,
|
||||
},
|
||||
ClientStatsSender::new(Some(stats_tx)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn report_stats(&mut self, recipient: Recipient) {
|
||||
let stats_report = self.stats.build_report();
|
||||
|
||||
let report_message = InputMessage::new_regular(
|
||||
recipient,
|
||||
stats_report.into(),
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
);
|
||||
if let Err(err) = self.report_tx.send(report_message).await {
|
||||
log::error!("Failed to report client stats: {:?}", err);
|
||||
} else {
|
||||
self.stats.reset();
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) {
|
||||
log::debug!("Started StatisticsControl with graceful shutdown support");
|
||||
|
||||
let mut stats_report_interval =
|
||||
tokio::time::interval(self.reporting_config.reporting_interval);
|
||||
let mut local_report_interval = tokio::time::interval(LOCAL_REPORT_INTERVAL);
|
||||
let mut snapshot_interval = tokio::time::interval(SNAPSHOT_INTERVAL);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
stats_event = self.stats_rx.recv() => match stats_event {
|
||||
Some(stats_event) => self.stats.handle_event(stats_event),
|
||||
None => {
|
||||
log::trace!("StatisticsControl: shutting down due to closed stats channel");
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = snapshot_interval.tick() => {
|
||||
self.stats.snapshot();
|
||||
}
|
||||
_ = stats_report_interval.tick(), if self.reporting_config.enabled && self.reporting_config.provider_address.is_some() => {
|
||||
// SAFTEY : this branch executes only if reporting is not none, so unwrapp is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.report_stats(self.reporting_config.provider_address.unwrap()).await;
|
||||
}
|
||||
|
||||
_ = local_report_interval.tick() => {
|
||||
self.stats.local_report(&mut task_client);
|
||||
}
|
||||
_ = task_client.recv_with_delay() => {
|
||||
log::trace!("StatisticsControl: Received shutdown");
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
task_client.recv_timeout().await;
|
||||
log::debug!("StatisticsControl: Exiting");
|
||||
}
|
||||
|
||||
pub(crate) fn start_with_shutdown(mut self, task_client: nym_task::TaskClient) {
|
||||
spawn_future(async move {
|
||||
self.run_with_shutdown(task_client).await;
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn create_and_start_with_shutdown(
|
||||
reporting_config: StatsReporting,
|
||||
client_type: String,
|
||||
client_stats_id: String,
|
||||
report_tx: InputMessageSender,
|
||||
task_client: nym_task::TaskClient,
|
||||
) -> ClientStatsSender {
|
||||
let (controller, sender) =
|
||||
Self::create(reporting_config, client_type, client_stats_id, report_tx);
|
||||
controller.start_with_shutdown(task_client);
|
||||
sender
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ pub struct TopologyReadPermit<'a> {
|
||||
permit: RwLockReadGuard<'a, Option<NymTopology>>,
|
||||
}
|
||||
|
||||
impl Deref for TopologyReadPermit<'_> {
|
||||
impl<'a> Deref for TopologyReadPermit<'a> {
|
||||
type Target = Option<NymTopology>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
||||
@@ -85,10 +85,15 @@ fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
|
||||
pub struct GeoAwareTopologyProvider {
|
||||
validator_client: nym_validator_client::client::NymApiClient,
|
||||
filter_on: GroupBy,
|
||||
client_version: String,
|
||||
}
|
||||
|
||||
impl GeoAwareTopologyProvider {
|
||||
pub fn new(mut nym_api_urls: Vec<Url>, filter_on: GroupBy) -> GeoAwareTopologyProvider {
|
||||
pub fn new(
|
||||
mut nym_api_urls: Vec<Url>,
|
||||
client_version: String,
|
||||
filter_on: GroupBy,
|
||||
) -> GeoAwareTopologyProvider {
|
||||
log::info!(
|
||||
"Creating geo-aware topology provider with filter on {}",
|
||||
filter_on
|
||||
@@ -100,13 +105,14 @@ impl GeoAwareTopologyProvider {
|
||||
nym_api_urls[0].clone(),
|
||||
),
|
||||
filter_on,
|
||||
client_version,
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_topology(&self) -> Option<NymTopology> {
|
||||
let mixnodes = match self
|
||||
.validator_client
|
||||
.get_all_basic_active_mixing_assigned_nodes()
|
||||
.get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
@@ -118,7 +124,7 @@ impl GeoAwareTopologyProvider {
|
||||
|
||||
let gateways = match self
|
||||
.validator_client
|
||||
.get_all_basic_entry_assigned_nodes()
|
||||
.get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
|
||||
@@ -35,11 +35,18 @@ pub struct NymApiTopologyProvider {
|
||||
|
||||
validator_client: nym_validator_client::client::NymApiClient,
|
||||
nym_api_urls: Vec<Url>,
|
||||
|
||||
client_version: String,
|
||||
currently_used_api: usize,
|
||||
}
|
||||
|
||||
impl NymApiTopologyProvider {
|
||||
pub fn new(config: Config, mut nym_api_urls: Vec<Url>, user_agent: Option<UserAgent>) -> Self {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
mut nym_api_urls: Vec<Url>,
|
||||
client_version: String,
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Self {
|
||||
nym_api_urls.shuffle(&mut thread_rng());
|
||||
|
||||
let validator_client = if let Some(user_agent) = user_agent {
|
||||
@@ -55,6 +62,7 @@ impl NymApiTopologyProvider {
|
||||
config,
|
||||
validator_client,
|
||||
nym_api_urls,
|
||||
client_version,
|
||||
currently_used_api: 0,
|
||||
}
|
||||
}
|
||||
@@ -91,7 +99,7 @@ impl NymApiTopologyProvider {
|
||||
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
|
||||
let mixnodes = match self
|
||||
.validator_client
|
||||
.get_all_basic_active_mixing_assigned_nodes()
|
||||
.get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
@@ -103,7 +111,7 @@ impl NymApiTopologyProvider {
|
||||
|
||||
let gateways = match self
|
||||
.validator_client
|
||||
.get_all_basic_entry_assigned_nodes()
|
||||
.get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
|
||||
@@ -212,29 +212,12 @@ pub enum ClientCoreError {
|
||||
}
|
||||
|
||||
/// Set of messages that the client can send to listeners via the task manager
|
||||
#[derive(Debug)]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ClientCoreStatusMessage {
|
||||
// NOTE: The nym-connect frontend listens for these strings, so don't change them until we have a more robust mechanism in place
|
||||
#[error("The connected gateway is slow, or the connection to it is slow")]
|
||||
GatewayIsSlow,
|
||||
// NOTE: The nym-connect frontend listens for these strings, so don't change them until we have a more robust mechanism in place
|
||||
#[error("The connected gateway is very slow, or the connection to it is very slow")]
|
||||
GatewayIsVerySlow,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ClientCoreStatusMessage {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ClientCoreStatusMessage::GatewayIsSlow => write!(
|
||||
f,
|
||||
"The connected gateway is slow, or the connection to it is slow"
|
||||
),
|
||||
ClientCoreStatusMessage::GatewayIsVerySlow => write!(
|
||||
f,
|
||||
"The connected gateway is very slow, or the connection to it is very slow"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl nym_task::TaskStatusEvent for ClientCoreStatusMessage {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use futures::{SinkExt, StreamExt};
|
||||
use log::{debug, info, trace, warn};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_client::GatewayClient;
|
||||
use nym_topology::gateway;
|
||||
use nym_topology::{gateway, mix};
|
||||
use nym_validator_client::client::IdentityKeyRef;
|
||||
use nym_validator_client::UserAgent;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
@@ -82,7 +82,6 @@ pub async fn current_gateways<R: Rng>(
|
||||
rng: &mut R,
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
minimum_performance: u8,
|
||||
) -> Result<Vec<gateway::LegacyNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
@@ -95,27 +94,44 @@ pub async fn current_gateways<R: Rng>(
|
||||
|
||||
log::debug!("Fetching list of gateways from: {nym_api}");
|
||||
|
||||
let gateways = client.get_all_basic_entry_assigned_nodes().await?;
|
||||
info!("nym api reports {} gateways", gateways.len());
|
||||
|
||||
let gateways = client.get_all_basic_entry_assigned_nodes(None).await?;
|
||||
log::debug!("Found {} gateways", gateways.len());
|
||||
log::trace!("Gateways: {:#?}", gateways);
|
||||
|
||||
let valid_gateways = gateways
|
||||
.iter()
|
||||
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<gateway::LegacyNode>>();
|
||||
log::debug!("After checking validity: {}", valid_gateways.len());
|
||||
log::trace!("Valid gateways: {:#?}", valid_gateways);
|
||||
|
||||
log::info!(
|
||||
"and {} after validity and performance filtering",
|
||||
valid_gateways.len()
|
||||
);
|
||||
log::info!("nym-api reports {} valid gateways", valid_gateways.len());
|
||||
|
||||
Ok(valid_gateways)
|
||||
}
|
||||
|
||||
pub async fn current_mixnodes<R: Rng>(
|
||||
rng: &mut R,
|
||||
nym_apis: &[Url],
|
||||
) -> Result<Vec<mix::LegacyNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
|
||||
|
||||
log::trace!("Fetching list of mixnodes from: {nym_api}");
|
||||
|
||||
let mixnodes = client
|
||||
.get_all_basic_active_mixing_assigned_nodes(None)
|
||||
.await?;
|
||||
let valid_mixnodes = mixnodes
|
||||
.iter()
|
||||
.filter_map(|mixnode| mixnode.try_into().ok())
|
||||
.collect::<Vec<mix::LegacyNode>>();
|
||||
|
||||
Ok(valid_mixnodes)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
||||
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
|
||||
|
||||
@@ -29,7 +29,6 @@ nym-crypto = { path = "../../crypto" }
|
||||
nym-gateway-requests = { path = "../../gateway-requests" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
nym-statistics-common = { path = "../../statistics" }
|
||||
nym-pemstore = { path = "../../pemstore" }
|
||||
nym-validator-client = { path = "../validator-client", default-features = false }
|
||||
nym-task = { path = "../../task" }
|
||||
|
||||
@@ -87,10 +87,8 @@ impl ClientBandwidth {
|
||||
|
||||
if remaining < 0 {
|
||||
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
|
||||
} else if remaining < 1_000_000 {
|
||||
tracing::info!("remaining bandwidth: {remaining_bi2}");
|
||||
} else {
|
||||
tracing::debug!("remaining bandwidth: {remaining_bi2}");
|
||||
tracing::info!("remaining bandwidth: {remaining_bi2}");
|
||||
}
|
||||
|
||||
self.inner
|
||||
|
||||
@@ -25,8 +25,6 @@ use nym_gateway_requests::{
|
||||
CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION,
|
||||
};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_statistics_common::clients::connection::ConnectionStatsEvent;
|
||||
use nym_statistics_common::clients::ClientStatsSender;
|
||||
use nym_task::TaskClient;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use rand::rngs::OsRng;
|
||||
@@ -96,7 +94,6 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
connection: SocketState,
|
||||
packet_router: PacketRouter,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
stats_reporter: ClientStatsSender,
|
||||
|
||||
// currently unused (but populated)
|
||||
negotiated_protocol: Option<u8>,
|
||||
@@ -106,7 +103,6 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
}
|
||||
|
||||
impl<C, St> GatewayClient<C, St> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
cfg: GatewayClientConfig,
|
||||
gateway_config: GatewayConfig,
|
||||
@@ -115,7 +111,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
shared_key: Option<Arc<SharedGatewayKey>>,
|
||||
packet_router: PacketRouter,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
stats_reporter: ClientStatsSender,
|
||||
task_client: TaskClient,
|
||||
) -> Self {
|
||||
GatewayClient {
|
||||
@@ -129,7 +124,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
connection: SocketState::NotConnected,
|
||||
packet_router,
|
||||
bandwidth_controller,
|
||||
stats_reporter,
|
||||
negotiated_protocol: None,
|
||||
task_client,
|
||||
}
|
||||
@@ -139,10 +133,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.gateway_identity
|
||||
}
|
||||
|
||||
pub fn shared_key(&self) -> Option<Arc<SharedGatewayKey>> {
|
||||
self.shared_key.clone()
|
||||
}
|
||||
|
||||
pub fn ws_fd(&self) -> Option<RawFd> {
|
||||
match &self.connection {
|
||||
SocketState::Available(conn) => ws_fd(conn.as_ref()),
|
||||
@@ -412,7 +402,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
Some(_) => {
|
||||
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
info!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -724,7 +714,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
{
|
||||
// TODO: make it configurable
|
||||
const TICKETS_TO_SPEND: u32 = 1;
|
||||
const MIXNET_TICKET: TicketType = TicketType::V1MixnetEntry;
|
||||
|
||||
if !self.authenticated {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
@@ -761,23 +750,14 @@ impl<C, St> GatewayClient<C, St> {
|
||||
let prepared_credential = self
|
||||
.unchecked_bandwidth_controller()
|
||||
.prepare_ecash_ticket(
|
||||
MIXNET_TICKET,
|
||||
TicketType::V1MixnetEntry,
|
||||
self.gateway_identity.to_bytes(),
|
||||
TICKETS_TO_SPEND,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match self.claim_ecash_bandwidth(prepared_credential.data).await {
|
||||
Ok(_) => {
|
||||
self.stats_reporter.report(
|
||||
ConnectionStatsEvent::TicketSpent {
|
||||
typ: MIXNET_TICKET,
|
||||
amount: TICKETS_TO_SPEND,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
error!("failed to claim ecash bandwidth with the gateway...: {err}");
|
||||
if err.is_ticket_replay() {
|
||||
@@ -996,6 +976,24 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[deprecated(note = "this method does not deal with upgraded keys for legacy clients")]
|
||||
pub async fn authenticate_and_start(
|
||||
&mut self,
|
||||
) -> Result<AuthenticationResponse, GatewayClientError>
|
||||
where
|
||||
C: DkgQueryClient + Send + Sync,
|
||||
St: CredentialStorage,
|
||||
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
let shared_key = self.perform_initial_authentication().await?;
|
||||
self.claim_initial_bandwidth().await?;
|
||||
|
||||
// this call is NON-blocking
|
||||
self.start_listening_for_mixnet_messages()?;
|
||||
|
||||
Ok(shared_key)
|
||||
}
|
||||
}
|
||||
|
||||
// type alias for an ease of use
|
||||
@@ -1032,7 +1030,6 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
connection: SocketState::NotConnected,
|
||||
packet_router,
|
||||
bandwidth_controller: None,
|
||||
stats_reporter: ClientStatsSender::new(None),
|
||||
negotiated_protocol: None,
|
||||
task_client,
|
||||
}
|
||||
@@ -1042,7 +1039,6 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
self,
|
||||
packet_router: PacketRouter,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
stats_reporter: ClientStatsSender,
|
||||
task_client: TaskClient,
|
||||
) -> GatewayClient<C, St> {
|
||||
// invariants that can't be broken
|
||||
@@ -1062,7 +1058,6 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
connection: self.connection,
|
||||
packet_router,
|
||||
bandwidth_controller,
|
||||
stats_reporter,
|
||||
negotiated_protocol: self.negotiated_protocol,
|
||||
task_client,
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user