Compare commits
310 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a98306d340 | |||
| b68e13f0f2 | |||
| 46fe1bc819 | |||
| 37ae72d8ec | |||
| 0b58b6f728 | |||
| e709e30e43 | |||
| 29b405f813 | |||
| f5d22a66f6 | |||
| bf7cd15428 | |||
| 757da6a456 | |||
| 62c581a9ae | |||
| 32e06e19e7 | |||
| fd1b524037 | |||
| 36d0adfe92 | |||
| fcf782674c | |||
| 4b35c36299 | |||
| ca7cbac320 | |||
| e410aecf40 | |||
| ae76335c31 | |||
| dac7f1f83c | |||
| 31ff3645c5 | |||
| 6dd3b78a74 | |||
| 0ab14f7041 | |||
| 9c6310264e | |||
| aa37bfb7ff | |||
| 66c2454775 | |||
| 670f383faa | |||
| 9714351fd8 | |||
| 7352499328 | |||
| 06717037e5 | |||
| 1a7aa2ce90 | |||
| c594361cd1 | |||
| df52f10f52 | |||
| 6a96d8205b | |||
| 22793bc45e | |||
| 6eb8f29235 | |||
| c9ef46c51d | |||
| f1024bc976 | |||
| 1aec4c2f8e | |||
| 1b79107726 | |||
| a69b473ba1 | |||
| fe01c922c0 | |||
| 4b0fbc663a | |||
| 4b292ca142 | |||
| 9262e24892 | |||
| e0c74c5eb0 | |||
| a293d6da7d | |||
| f12a554e85 | |||
| 8c3a797750 | |||
| 00d0ae0b5b | |||
| de0ae687ef | |||
| 42c051dfa3 | |||
| 26f4dd8f39 | |||
| 2d37c33a3d | |||
| f1be6ae788 | |||
| c13b4aa745 | |||
| 68eae18b8b | |||
| 28dc7cae4d | |||
| 37f3ef58a3 | |||
| 52f98de73b | |||
| 6d63ba1f4c | |||
| 6170ca2a14 | |||
| 2cc59aadc5 | |||
| 89dc865ec6 | |||
| 6d1d9d58a5 | |||
| 3057721845 | |||
| 54c7a01482 | |||
| 76993a9b94 | |||
| b00e1f2fff | |||
| ef7974fde9 | |||
| a488a1b489 | |||
| bb5b43492a | |||
| f9e2311574 | |||
| 50d768976f | |||
| 3825d5f173 | |||
| 9b076197b1 | |||
| 7b96adf7a8 | |||
| 18d271f481 | |||
| 752c7915b3 | |||
| 6c01c9fceb | |||
| 4e7b4715b0 | |||
| 4fdbcb051a | |||
| 47c6006bb7 | |||
| dcfd0f77ad | |||
| 78fb779010 | |||
| b4544c2b48 | |||
| 90e07d9980 | |||
| b8479e1cde | |||
| 37e3a101b1 | |||
| 45a1074377 | |||
| 1b9af19e20 | |||
| 7a339d4c4d | |||
| 568268d39b | |||
| 9c5847dc67 | |||
| 22db132c09 | |||
| 5496cce5c9 | |||
| 8de37eb6c9 | |||
| 40a7a87c61 | |||
| 95ee3a7c7d | |||
| 4736f1eb52 | |||
| c23e139eaa | |||
| bfcb4c79bb | |||
| 2933732225 | |||
| 2e059865a1 | |||
| 06c0c36c67 | |||
| bdc0f5022d | |||
| b742ace7e0 | |||
| 82a9563ca0 | |||
| ef25480c20 | |||
| bcce854a8b | |||
| f960bfa91b | |||
| 96e3ff2af9 | |||
| 4f991061dd | |||
| d73b7b7127 | |||
| 440aadf124 | |||
| e5aef76256 | |||
| 6acc54d2bc | |||
| d126d8e5a0 | |||
| ab6e08dd13 | |||
| e09066858c | |||
| 6b2bb3029b | |||
| 842ce93a60 | |||
| ce26105986 | |||
| cc95358385 | |||
| cc04a09ed7 | |||
| ae47d53f0c | |||
| e0ff09f323 | |||
| 10707fd2c5 | |||
| 9bdd2af14c | |||
| 228ef8b158 | |||
| d820131d2c | |||
| 054715a600 | |||
| 3f560180b7 | |||
| f62dbbdae0 | |||
| edecc4ba01 | |||
| 1a4d64a0e5 | |||
| 4dcc568ec2 | |||
| 468835e3a2 | |||
| 28a866e26d | |||
| 350d244032 | |||
| 17ca000782 | |||
| 58c0e289c2 | |||
| 6d8edc4bc7 | |||
| a44cdf1c7c | |||
| 6b8a6283a4 | |||
| 94151965bb | |||
| e8ca490db1 | |||
| fe7470ea44 | |||
| 21d52244cb | |||
| c0c58026a8 | |||
| 0fe863c889 | |||
| 4e5d88f64c | |||
| 1559f6a912 | |||
| 766024be27 | |||
| 5ba181b118 | |||
| 76fc9f4a90 | |||
| 8ca6af7c86 | |||
| 45e14a7fb1 | |||
| a38917cb9b | |||
| cf8a399089 | |||
| ba01820586 | |||
| 8c799b2976 | |||
| de4fb6291c | |||
| 81fd37e5c0 | |||
| 219f3af967 | |||
| aea7442525 | |||
| 1525aed657 | |||
| 943b5fa8bc | |||
| 71e0c025c6 | |||
| 239c6c701b | |||
| 91d0b7bdad | |||
| 99b28b2b6f | |||
| 5627ada57e | |||
| 4e1228fff0 | |||
| 04be5624fa | |||
| a6fe1b1de7 | |||
| c5971d0e9d | |||
| 06dd74ba34 | |||
| c6a0256b03 | |||
| d04b61a88b | |||
| 70a119ac58 | |||
| e2fe3a60a6 | |||
| 71301ee0cc | |||
| aba6c9d4ac | |||
| c617bbb240 | |||
| 1f8144eb11 | |||
| 694135c81b | |||
| e815f08505 | |||
| f402da8e60 | |||
| 34a500d0a2 | |||
| aac983d922 | |||
| 577675bab3 | |||
| ec015618cd | |||
| ef52f25564 | |||
| 010b013094 | |||
| c503a5f0e8 | |||
| 781afd3522 | |||
| 58083df0b0 | |||
| 4e8d29d0c5 | |||
| 66797efa80 | |||
| 5a26fa262e | |||
| 73a34935ae | |||
| fa40acbeca | |||
| a8086675d9 | |||
| 0453345d65 | |||
| b56d9505e6 | |||
| bdacc72003 | |||
| 9eca9efd82 | |||
| 386e1790dd | |||
| d07f9c8fad | |||
| 0dc071daeb | |||
| babf113fe5 | |||
| cc74d218fc | |||
| dea8a287e6 | |||
| 10951d4cd3 | |||
| 872c25bfcc | |||
| 5acce42c64 | |||
| fc5d310935 | |||
| 4848d081d0 | |||
| b3452ede77 | |||
| a44819b14c | |||
| 5455110810 | |||
| de27778192 | |||
| a0178d28f7 | |||
| 3e42160426 | |||
| 2f752a6c42 | |||
| 806f807f02 | |||
| 1400db6156 | |||
| 673a3e45d3 | |||
| d9c2f6ebda | |||
| e24e094711 | |||
| 0d7487f530 | |||
| 378f32e6d7 | |||
| 3e9b8d237f | |||
| f5a4dbc555 | |||
| 4480534e4d | |||
| d355f9d752 | |||
| 9f3a370496 | |||
| e4adc5d954 | |||
| 00373b70e2 | |||
| 65f2017422 | |||
| 192f258463 | |||
| a5eee7444b | |||
| 6abd7e7352 | |||
| 3306ca5357 | |||
| 9c2ccead0e | |||
| b7aeb51362 | |||
| e9e725defe | |||
| c74494a21d | |||
| 54f6c98c22 | |||
| 846484bbb4 | |||
| fb3f5501ba | |||
| e8a607f520 | |||
| f5f6df9eaf | |||
| c647ab5605 | |||
| 416c21a42e | |||
| fd5a95fa4d | |||
| c61df79182 | |||
| 08559a7660 | |||
| 6dce55a99b | |||
| bc0b89b31c | |||
| 67c32faa11 | |||
| aa0d15ee67 | |||
| 4f0974fcf1 | |||
| bd2174641e | |||
| 59b62fabc9 | |||
| e6f4bae895 | |||
| d71742af32 | |||
| 3b7c07e249 | |||
| 3b429dde69 | |||
| 3a29c296da | |||
| 8544c54f8f | |||
| 9f9639950b | |||
| 111a0b20b6 | |||
| 67b300d0b8 | |||
| f6800aff0a | |||
| 0c7c927ca2 | |||
| a69c8b1660 | |||
| f3ea310a46 | |||
| 92f9ff035d | |||
| 17d11f201e | |||
| fef7e42eb4 | |||
| ceeeb6211b | |||
| cd77b1032f | |||
| 6bbb14f12f | |||
| de8030d85a | |||
| e18e64bf21 | |||
| a50c9ac3fb | |||
| 2235a6e147 | |||
| db6defa122 | |||
| df7768dec0 | |||
| f3a449b7cc | |||
| cf21593ffa | |||
| 37c4a3aa36 | |||
| f0d8dabb9f | |||
| f105bcbafe | |||
| dc0f4af2c1 | |||
| 2a621e07a8 | |||
| 485aeebabd | |||
| 3b726bada9 | |||
| 1d1b2e17d2 | |||
| b5b2dbdfd8 | |||
| 82806f47d8 | |||
| c6f85cf23e | |||
| ed8de7234d | |||
| e25d83b047 | |||
| 9974d480b5 | |||
| 2211f13cdd | |||
| 4505f18a02 | |||
| a717a18948 |
@@ -8,10 +8,13 @@ on:
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
- 'nym-api/**'
|
||||
- 'nym-authenticator-client/**'
|
||||
- 'nym-credential-proxy/**'
|
||||
- 'nym-ip-packet-client/**'
|
||||
- 'nym-network-monitor/**'
|
||||
- 'nym-node/**'
|
||||
- 'nym-node-status-api/**'
|
||||
- 'nym-registration-client/**'
|
||||
- 'nym-statistics-api/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'nym-validator-rewarder/**'
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -10,13 +10,13 @@ env:
|
||||
|
||||
jobs:
|
||||
check-if-tag-exists:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
runs-on: arc-linux-latest-dind
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -6,16 +6,14 @@ on:
|
||||
paths:
|
||||
- "ts-packages/**"
|
||||
- "sdk/typescript/**"
|
||||
- "nym-connect/desktop/src/**"
|
||||
- "nym-connect/desktop/package.json"
|
||||
- "nym-wallet/src/**"
|
||||
- "nym-wallet/package.json"
|
||||
- "explorer/**"
|
||||
- "explorer-v2/**"
|
||||
- ".github/workflows/ci-lint-typescript.yml"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: arc-linux-latest
|
||||
env:
|
||||
RUSTUP_PERMIT_COPY_RENAME: 1
|
||||
steps:
|
||||
@@ -25,6 +23,7 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
@@ -37,14 +36,12 @@ jobs:
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.23.7"
|
||||
go-version: "1.24.6"
|
||||
|
||||
- name: Install
|
||||
run: yarn
|
||||
@@ -52,7 +49,11 @@ jobs:
|
||||
- name: Build packages
|
||||
run: yarn build:ci
|
||||
|
||||
- name: Install again
|
||||
run: yarn
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
|
||||
- name: Typecheck with tsc
|
||||
run: yarn tsc
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-linux
|
||||
runs-on: arc-linux-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.24.6"
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: SonarQube Scan
|
||||
uses: SonarSource/sonarqube-scan-action@v5
|
||||
uses: SonarSource/sonarqube-scan-action@v6
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
# Upload entire repository
|
||||
path: './ppa'
|
||||
|
||||
@@ -8,6 +8,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/first-interaction@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: 'Thank you for raising this issue'
|
||||
pr-message: 'Thank you for making this first PR'
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue_message: 'Thank you for raising this issue'
|
||||
pr_message: 'Thank you for making this first PR'
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: arc-ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -17,10 +17,13 @@ jobs:
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Install Rust stable
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
@@ -29,9 +32,9 @@ jobs:
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.23.7"
|
||||
go-version: "1.24.6"
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
|
||||
|
||||
@@ -3,11 +3,6 @@ name: Build and upload Node Status agent container to harbor.nymte.ch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gateway_probe_git_ref:
|
||||
type: string
|
||||
default: nym-vpn-core-v1.4.0
|
||||
required: true
|
||||
description: Which gateway probe git ref to build the image with
|
||||
release_image:
|
||||
description: 'Tag image as a release'
|
||||
required: true
|
||||
@@ -43,16 +38,6 @@ jobs:
|
||||
VERSION=$(yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml)
|
||||
echo "result=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: cleanup-gateway-probe-ref
|
||||
id: cleanup_gateway_probe_ref
|
||||
run: |
|
||||
GATEWAY_PROBE_GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }}
|
||||
GIT_REF_SLUG="${GATEWAY_PROBE_GIT_REF//\//-}"
|
||||
echo "git_ref=${GIT_REF_SLUG}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set GIT_TAG variable
|
||||
run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Initialize RELEASE_TAG
|
||||
run: echo "RELEASE_TAG=" >> $GITHUB_ENV
|
||||
|
||||
@@ -61,24 +46,12 @@ jobs:
|
||||
run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV
|
||||
|
||||
- name: Set IMAGE_NAME_AND_TAGS variable
|
||||
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
|
||||
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
|
||||
|
||||
- name: New env vars
|
||||
run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
|
||||
|
||||
# - name: Remove existing tag if exists
|
||||
# run: |
|
||||
# if git rev-parse $${{ env.GIT_TAG }} >/dev/null 2>&1; then
|
||||
# git push --delete origin $${{ env.GIT_TAG }}
|
||||
# git tag -d $${{ env.GIT_TAG }}
|
||||
# fi
|
||||
|
||||
# - name: Create tag
|
||||
# run: |
|
||||
# git tag -a $${{ env.GIT_TAG }} -m "Version ${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}"
|
||||
# git push origin $${{ env.GIT_TAG }}
|
||||
run: echo "RELEASE_TAG='$RELEASE_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-api/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.47.1
|
||||
uses: mikefarah/yq@v4.48.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
+150
@@ -4,6 +4,156 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2026.1-niolo] (2026-01-13)
|
||||
|
||||
- bugfix: mozzarella -> niolo config migration ([#6259])
|
||||
- chore: remove run DKG migration ([#6253])
|
||||
- bugfix: reexposed 'derive_extended_private_key' ([#6247])
|
||||
- Bump js-yaml from 3.14.1 to 3.14.2 in /sdk/typescript/codegen/contract-clients ([#6231])
|
||||
- Statistics API v2 ([#6227])
|
||||
- Bump golang.org/x/crypto from 0.39.0 to 0.45.0 in /nym-gateway-probe/netstack_ping ([#6220])
|
||||
- Update chain registry link ([#6219])
|
||||
- Bump glob from 10.3.4 to 10.5.0 in /documentation/scripts/post-process ([#6216])
|
||||
- Bump js-yaml from 4.1.0 to 4.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6215])
|
||||
- gateway-probe fixes for run-local ([#6212])
|
||||
- chore: updated default endpoint for retrieving attestation.json ([#6207])
|
||||
- chore: remove support for legacy mixnode within the performance contract ([#6205])
|
||||
- feat: upgrade mode: VPN adjustments ([#6189])
|
||||
- Bump min-document from 2.19.0 to 2.19.1 ([#6181])
|
||||
- Bump next from 15.4.1 to 15.4.7 in /nym-node-status-api/nym-node-status-ui ([#6180])
|
||||
- feat: merge intermediate upgrade mode changes ([#6174])
|
||||
- Add weighted scoring to NS API ([#6144])
|
||||
- build(deps): bump mikefarah/yq from 4.47.1 to 4.48.1 ([#6107])
|
||||
- build(deps): bump SonarSource/sonarqube-scan-action from 5 to 6 in /.github/workflows ([#6068])
|
||||
- build(deps): bump tar-fs from 3.0.9 to 3.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6063])
|
||||
- build(deps): bump ammonia from 4.1.1 to 4.1.2 ([#6057])
|
||||
- build(deps): bump tower-http from 0.5.2 to 0.6.6 ([#6030])
|
||||
- build(deps): bump actions/setup-go from 5 to 6 ([#6013])
|
||||
- build(deps): bump next from 14.2.28 to 14.2.32 ([#5996])
|
||||
- build(deps): bump tracing-subscriber from 0.3.19 to 0.3.20 ([#5993])
|
||||
- build(deps): bump actions/upload-pages-artifact from 3 to 4 ([#5992])
|
||||
|
||||
[#6259]: https://github.com/nymtech/nym/pull/6259
|
||||
[#6253]: https://github.com/nymtech/nym/pull/6253
|
||||
[#6247]: https://github.com/nymtech/nym/pull/6247
|
||||
[#6231]: https://github.com/nymtech/nym/pull/6231
|
||||
[#6227]: https://github.com/nymtech/nym/pull/6227
|
||||
[#6220]: https://github.com/nymtech/nym/pull/6220
|
||||
[#6219]: https://github.com/nymtech/nym/pull/6219
|
||||
[#6216]: https://github.com/nymtech/nym/pull/6216
|
||||
[#6215]: https://github.com/nymtech/nym/pull/6215
|
||||
[#6212]: https://github.com/nymtech/nym/pull/6212
|
||||
[#6207]: https://github.com/nymtech/nym/pull/6207
|
||||
[#6205]: https://github.com/nymtech/nym/pull/6205
|
||||
[#6189]: https://github.com/nymtech/nym/pull/6189
|
||||
[#6181]: https://github.com/nymtech/nym/pull/6181
|
||||
[#6180]: https://github.com/nymtech/nym/pull/6180
|
||||
[#6174]: https://github.com/nymtech/nym/pull/6174
|
||||
[#6144]: https://github.com/nymtech/nym/pull/6144
|
||||
[#6107]: https://github.com/nymtech/nym/pull/6107
|
||||
[#6068]: https://github.com/nymtech/nym/pull/6068
|
||||
[#6063]: https://github.com/nymtech/nym/pull/6063
|
||||
[#6057]: https://github.com/nymtech/nym/pull/6057
|
||||
[#6030]: https://github.com/nymtech/nym/pull/6030
|
||||
[#6013]: https://github.com/nymtech/nym/pull/6013
|
||||
[#5996]: https://github.com/nymtech/nym/pull/5996
|
||||
[#5993]: https://github.com/nymtech/nym/pull/5993
|
||||
[#5992]: https://github.com/nymtech/nym/pull/5992
|
||||
|
||||
## [2025.21-mozzarella] (2025-11-25)
|
||||
|
||||
- [bugfix] Tunnel not waiting on MixnetClient to shut down cleanly ([#6225])
|
||||
- bugfix: fix credential proxy upgrade mode attestation url arg ([#6202])
|
||||
- HTTP API resilience enable & domain rotation conditions ([#6200])
|
||||
- Remove debug feature from http-macro spec in gateway probe ([#6195])
|
||||
- DNS relibility and troubleshooting ([#6179])
|
||||
- [bugfix] Distinguish authenticator errors by credential spent ([#6176])
|
||||
- Typescript SDK 1.4.1 ([#6146])
|
||||
- Enable URL rotation and retries for mixnet gateway init ([#6126])
|
||||
- Feature/credential proxy jwt ([#5957])
|
||||
|
||||
[#6225]: https://github.com/nymtech/nym/pull/6225
|
||||
[#6202]: https://github.com/nymtech/nym/pull/6202
|
||||
[#6200]: https://github.com/nymtech/nym/pull/6200
|
||||
[#6195]: https://github.com/nymtech/nym/pull/6195
|
||||
[#6179]: https://github.com/nymtech/nym/pull/6179
|
||||
[#6176]: https://github.com/nymtech/nym/pull/6176
|
||||
[#6146]: https://github.com/nymtech/nym/pull/6146
|
||||
[#6126]: https://github.com/nymtech/nym/pull/6126
|
||||
[#5957]: https://github.com/nymtech/nym/pull/5957
|
||||
|
||||
## [2025.20-leerdammer] (2025-11-12)
|
||||
|
||||
- Max/tweak ts sdk actions ([#6185])
|
||||
- chore: resolve clippy 1.91 warnings ([#6168])
|
||||
- [chore] Remove unused dependencies ([#6151])
|
||||
- Use typed-builder for registration client builder config ([#6150])
|
||||
- tommy is too quick ([#6149])
|
||||
- configurable mixnet client startup timeout ([#6148])
|
||||
- [Feature/operators]: QUIC bridge deployment script v2 ([#6145])
|
||||
- Bugfix: Add circuit breaker ([#6143])
|
||||
- bugfix: update internal owner address in transferred share ([#6139])
|
||||
- Update quic_bridge_deployment.sh for IPv4 and .deb package ([#6138])
|
||||
- feat: expose more explicit new_with_fronted_urls builder for http API client ([#6136])
|
||||
- bugfix: update stored epoch share when changing ownership ([#6135])
|
||||
- Domain fronting ([#6134])
|
||||
- bugfix: update stored epoch share when changing announce address ([#6131])
|
||||
|
||||
[#6185]: https://github.com/nymtech/nym/pull/6185
|
||||
[#6168]: https://github.com/nymtech/nym/pull/6168
|
||||
[#6151]: https://github.com/nymtech/nym/pull/6151
|
||||
[#6150]: https://github.com/nymtech/nym/pull/6150
|
||||
[#6149]: https://github.com/nymtech/nym/pull/6149
|
||||
[#6148]: https://github.com/nymtech/nym/pull/6148
|
||||
[#6145]: https://github.com/nymtech/nym/pull/6145
|
||||
[#6143]: https://github.com/nymtech/nym/pull/6143
|
||||
[#6139]: https://github.com/nymtech/nym/pull/6139
|
||||
[#6138]: https://github.com/nymtech/nym/pull/6138
|
||||
[#6136]: https://github.com/nymtech/nym/pull/6136
|
||||
[#6135]: https://github.com/nymtech/nym/pull/6135
|
||||
[#6134]: https://github.com/nymtech/nym/pull/6134
|
||||
[#6131]: https://github.com/nymtech/nym/pull/6131
|
||||
|
||||
## [2025.19-kase] (2025-10-30)
|
||||
|
||||
- update ns agent workflow ([#6154])
|
||||
- Cherry pick - request #6143 from nymtech/bugfix/mix-tx-closed-v2 ([#6153])
|
||||
- bugfix: nym-credential-proxy query params parsing regression ([#6121])
|
||||
- bugfix: revert some dep updates introduced in #6043 ([#6120])
|
||||
- Skip ipv6 metadata endpoint request ([#6118])
|
||||
- update to no longer use 1mb files ([#6117])
|
||||
- chore: restore pending dkg contract state migration ([#6116])
|
||||
- Revert "Propagate cancel token to mixnet client" ([#6115])
|
||||
- Update dirs to 6.0 ([#6109])
|
||||
- Propagate cancel token to mixnet client ([#6105])
|
||||
- bugfix: retrieve and update ticketbook in the same query ([#6101])
|
||||
- bugfix: include network name in the default gateway probe config path ([#6100])
|
||||
- Bugfix/incompatibility fixes ([#6099])
|
||||
- [DOCs/operators] QUIC deployment script & docs ([#6098])
|
||||
- bugfix: testnet manager 02sql migration ([#6096])
|
||||
- feat: move gateway probe to monorepo (and update to rust edition 2024) ([#6094])
|
||||
- bugfix: use custom topology provider for list of init gateways ([#6092])
|
||||
- Max/fix wasm client + build commands ([#6043])
|
||||
|
||||
[#6154]: https://github.com/nymtech/nym/pull/6154
|
||||
[#6153]: https://github.com/nymtech/nym/pull/6153
|
||||
[#6121]: https://github.com/nymtech/nym/pull/6121
|
||||
[#6120]: https://github.com/nymtech/nym/pull/6120
|
||||
[#6118]: https://github.com/nymtech/nym/pull/6118
|
||||
[#6117]: https://github.com/nymtech/nym/pull/6117
|
||||
[#6116]: https://github.com/nymtech/nym/pull/6116
|
||||
[#6115]: https://github.com/nymtech/nym/pull/6115
|
||||
[#6109]: https://github.com/nymtech/nym/pull/6109
|
||||
[#6105]: https://github.com/nymtech/nym/pull/6105
|
||||
[#6101]: https://github.com/nymtech/nym/pull/6101
|
||||
[#6100]: https://github.com/nymtech/nym/pull/6100
|
||||
[#6099]: https://github.com/nymtech/nym/pull/6099
|
||||
[#6098]: https://github.com/nymtech/nym/pull/6098
|
||||
[#6096]: https://github.com/nymtech/nym/pull/6096
|
||||
[#6094]: https://github.com/nymtech/nym/pull/6094
|
||||
[#6092]: https://github.com/nymtech/nym/pull/6092
|
||||
[#6043]: https://github.com/nymtech/nym/pull/6043
|
||||
|
||||
## [2025.18-jarlsberg] (2025-10-14)
|
||||
|
||||
- ns-api: add descriptions to dVPN gateway responses ([#6102])
|
||||
|
||||
Generated
+105
-105
@@ -133,9 +133,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "ammonia"
|
||||
version = "4.1.1"
|
||||
version = "4.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f"
|
||||
checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever",
|
||||
@@ -1283,7 +1283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2262,7 +2262,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2579,7 +2579,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "extension-storage"
|
||||
version = "1.4.0-rc.0"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"console_error_panic_hook",
|
||||
@@ -3965,7 +3965,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4374,11 +4374,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4478,7 +4478,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mix-fetch-wasm"
|
||||
version = "1.4.0-rc.0"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
@@ -4488,6 +4488,7 @@ dependencies = [
|
||||
"nym-ordered-buffer",
|
||||
"nym-service-providers-common",
|
||||
"nym-socks5-requests",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
@@ -4740,6 +4741,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
@@ -4824,7 +4834,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.67"
|
||||
version = "1.1.71"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4895,7 +4905,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tower-http 0.5.2",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"ts-rs",
|
||||
"url",
|
||||
@@ -4987,6 +4997,7 @@ dependencies = [
|
||||
"nym-network-defaults",
|
||||
"nym-service-provider-requests-common",
|
||||
"nym-sphinx",
|
||||
"nym-test-utils",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"semver 1.0.26",
|
||||
@@ -4994,6 +5005,7 @@ dependencies = [
|
||||
"sha2 0.10.9",
|
||||
"strum_macros",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
@@ -5002,21 +5014,16 @@ name = "nym-bandwidth-controller"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bip39",
|
||||
"log",
|
||||
"nym-credential-storage",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-time",
|
||||
"nym-network-defaults",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5050,7 +5057,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.64"
|
||||
version = "1.1.68"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -5133,7 +5140,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.68"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -5283,7 +5290,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-wasm"
|
||||
version = "1.4.0-rc.0"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -5462,8 +5469,11 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-signer-check",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-network-defaults",
|
||||
"nym-pemstore",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.12.22",
|
||||
@@ -5477,7 +5487,7 @@ dependencies = [
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-http 0.5.2",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"url",
|
||||
"utoipa",
|
||||
@@ -5534,6 +5544,7 @@ dependencies = [
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-serde-helpers",
|
||||
"nym-upgrade-mode-check",
|
||||
"reqwest 0.12.22",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
@@ -5565,6 +5576,7 @@ dependencies = [
|
||||
"sqlx",
|
||||
"sqlx-pool-guard",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -5600,12 +5612,13 @@ dependencies = [
|
||||
"nym-api-requests",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-gateway-requests",
|
||||
"nym-gateway-storage",
|
||||
"nym-task",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"si-scale",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
@@ -5645,6 +5658,7 @@ dependencies = [
|
||||
"nym-compact-ecash",
|
||||
"nym-ecash-time",
|
||||
"nym-network-defaults",
|
||||
"nym-upgrade-mode-check",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"strum",
|
||||
@@ -5661,6 +5675,7 @@ dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"aes-gcm-siv",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"blake3",
|
||||
"bs58",
|
||||
@@ -5674,10 +5689,12 @@ dependencies = [
|
||||
"jwt-simple",
|
||||
"nym-pemstore",
|
||||
"nym-sphinx-types",
|
||||
"nym-test-utils",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"subtle-encoding",
|
||||
"thiserror 2.0.12",
|
||||
@@ -5792,7 +5809,6 @@ dependencies = [
|
||||
name = "nym-gateway"
|
||||
version = "1.1.36"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bincode",
|
||||
"bip39",
|
||||
@@ -5803,7 +5819,6 @@ dependencies = [
|
||||
"futures",
|
||||
"ipnetwork",
|
||||
"mock_instant",
|
||||
"nym-api-requests",
|
||||
"nym-authenticator-requests",
|
||||
"nym-client-core",
|
||||
"nym-credential-verification",
|
||||
@@ -5816,7 +5831,6 @@ dependencies = [
|
||||
"nym-id",
|
||||
"nym-ip-packet-router",
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnode-common",
|
||||
"nym-network-defaults",
|
||||
"nym-network-requester",
|
||||
"nym-node-metrics",
|
||||
@@ -5826,20 +5840,18 @@ dependencies = [
|
||||
"nym-statistics-common",
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"nym-types",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-validator-client",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-private-metadata-server",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"sha2 0.10.9",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
"zeroize",
|
||||
@@ -6050,6 +6062,7 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"wasmtimer",
|
||||
]
|
||||
@@ -6354,7 +6367,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.65"
|
||||
version = "1.1.69"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -6404,7 +6417,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.19.0"
|
||||
version = "1.23.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -6435,6 +6448,7 @@ dependencies = [
|
||||
"nym-bin-common",
|
||||
"nym-client-core-config-types",
|
||||
"nym-config",
|
||||
"nym-credential-verification",
|
||||
"nym-crypto",
|
||||
"nym-gateway",
|
||||
"nym-gateway-stats-storage",
|
||||
@@ -6477,7 +6491,7 @@ dependencies = [
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"toml 0.8.23",
|
||||
"tower-http 0.5.2",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-indicatif",
|
||||
"tracing-subscriber",
|
||||
@@ -6507,13 +6521,13 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"celes",
|
||||
"humantime",
|
||||
"humantime-serde",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"nym-exit-policy",
|
||||
"nym-http-api-client",
|
||||
"nym-noise-keys",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-wireguard-types",
|
||||
"rand_chacha 0.3.1",
|
||||
"schemars 0.8.22",
|
||||
@@ -6524,6 +6538,7 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
@@ -6546,7 +6561,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "4.0.10"
|
||||
version = "4.0.12"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@@ -6594,7 +6609,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tower-http 0.5.2",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-log 0.2.0",
|
||||
"tracing-subscriber",
|
||||
@@ -6642,7 +6657,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-tester-wasm"
|
||||
version = "1.3.0-rc.0"
|
||||
version = "1.3.1"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
@@ -6798,6 +6813,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"typed-builder",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -6930,7 +6946,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.68"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -7190,7 +7206,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-statistics-api"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -7204,13 +7220,12 @@ dependencies = [
|
||||
"nym-statistics-common",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-http 0.5.2",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
@@ -7377,18 +7392,21 @@ dependencies = [
|
||||
"jwt-simple",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"nym-test-utils",
|
||||
"reqwest 0.12.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-validator-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bip32",
|
||||
@@ -7562,17 +7580,10 @@ dependencies = [
|
||||
name = "nym-wireguard"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"dashmap",
|
||||
"defguard_wireguard_rs",
|
||||
"dyn-clone",
|
||||
"futures",
|
||||
"ip_network",
|
||||
"log",
|
||||
"nym-authenticator-requests",
|
||||
"nym-credential-verification",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
@@ -7583,11 +7594,9 @@ dependencies = [
|
||||
"nym-task",
|
||||
"nym-wireguard-types",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7615,7 +7624,7 @@ dependencies = [
|
||||
"nym-wireguard-private-metadata-shared",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-http 0.5.2",
|
||||
"tower-http",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
]
|
||||
@@ -7639,16 +7648,21 @@ version = "1.0.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"futures",
|
||||
"nym-credential-verification",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-private-metadata-client",
|
||||
"nym-wireguard-private-metadata-server",
|
||||
"nym-wireguard-private-metadata-shared",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower-http 0.5.2",
|
||||
"tower 0.5.2",
|
||||
"tower-http",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
@@ -7657,10 +7671,7 @@ name = "nym-wireguard-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
@@ -7669,7 +7680,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.29"
|
||||
version = "0.1.33"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7720,7 +7731,7 @@ dependencies = [
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-http 0.5.2",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"utoipa",
|
||||
@@ -7823,9 +7834,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.109"
|
||||
version = "0.9.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -8648,7 +8659,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2 0.5.10",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8809,17 +8820,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8830,15 +8832,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
@@ -8918,7 +8914,7 @@ dependencies = [
|
||||
"tokio-rustls 0.26.2",
|
||||
"tokio-util",
|
||||
"tower 0.5.2",
|
||||
"tower-http 0.6.6",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
@@ -9118,7 +9114,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10434,7 +10430,7 @@ dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix 1.0.8",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11053,9 +11049,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.5.2"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"bitflags 2.9.1",
|
||||
@@ -11067,33 +11063,17 @@ dependencies = [
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"iri-string",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"iri-string",
|
||||
"pin-project-lite",
|
||||
"tower 0.5.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11201,14 +11181,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.50.1",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
@@ -11244,7 +11224,7 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.46.0",
|
||||
"tracing-core",
|
||||
"tracing-log 0.1.4",
|
||||
"tracing-subscriber",
|
||||
@@ -11391,6 +11371,26 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d0dd654273fc253fde1df4172c31fb6615cf8b041d3a4008a028ef8b1119e66"
|
||||
dependencies = [
|
||||
"typed-builder-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder-macro"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016c26257f448222014296978b2c8456e2cad4de308c35bdb1e383acd569ef5b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
@@ -12226,7 +12226,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+10
-15
@@ -150,7 +150,7 @@ members = [
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/mixnet-connectivity-check",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
@@ -171,6 +171,7 @@ members = [
|
||||
default-members = [
|
||||
"clients/native",
|
||||
"clients/socks5",
|
||||
"nym-authenticator-client",
|
||||
"nym-api",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-node",
|
||||
@@ -215,7 +216,6 @@ base64 = "0.22.1"
|
||||
base85rs = "0.1.3"
|
||||
bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
bit-vec = "0.7.0" # can we unify those?
|
||||
bitvec = "1.0.0"
|
||||
blake3 = "1.7.0"
|
||||
bloomfilter = "3.0.1"
|
||||
@@ -243,13 +243,11 @@ criterion = "0.5"
|
||||
csv = "1.3.1"
|
||||
ctr = "0.9.1"
|
||||
cupid = "0.6.1"
|
||||
curve25519-dalek = "4.1"
|
||||
dashmap = "5.5.3"
|
||||
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
|
||||
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
|
||||
digest = "0.10.7"
|
||||
dirs = "6.0"
|
||||
doc-comment = "0.3"
|
||||
dotenvy = "0.15.6"
|
||||
dyn-clone = "1.0.19"
|
||||
ecdsa = "0.16"
|
||||
@@ -265,11 +263,8 @@ futures = "0.3.31"
|
||||
futures-util = "0.3"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
getset = "0.1.5"
|
||||
handlebars = "3.5.5"
|
||||
headers = "0.4.0"
|
||||
hex = "0.4.3"
|
||||
hex-literal = "0.3.3"
|
||||
hickory-resolver = "0.25"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12.1"
|
||||
@@ -293,12 +288,10 @@ lazy_static = "1.5.0"
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
log = "0.4"
|
||||
maxminddb = "0.23.0"
|
||||
mime = "0.3.17"
|
||||
moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.21.3"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
@@ -307,7 +300,6 @@ pem = "0.8"
|
||||
petgraph = "0.6.5"
|
||||
pin-project = "1.1"
|
||||
pnet_packet = "0.35.0"
|
||||
pin-project-lite = "0.2.16"
|
||||
publicsuffix = "2.3.0"
|
||||
proc_pidinfo = "0.1.3"
|
||||
quote = "1"
|
||||
@@ -315,13 +307,10 @@ rand = "0.8.5"
|
||||
rand_chacha = "0.3"
|
||||
rand_core = "0.6.3"
|
||||
rand_distr = "0.4"
|
||||
rand_pcg = "0.3.1"
|
||||
rand_seeder = "0.2.3"
|
||||
rayon = "1.5.1"
|
||||
regex = "1.10.6"
|
||||
reqwest = { version = "0.12.15", default-features = false }
|
||||
rs_merkle = "1.5.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.22"
|
||||
semver = "1.0.26"
|
||||
serde = "1.0.219"
|
||||
@@ -358,16 +347,17 @@ tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = "0.7.15"
|
||||
toml = "0.8.22"
|
||||
tower = "0.5.2"
|
||||
tower-http = "0.5.2"
|
||||
tower-http = "0.6.6"
|
||||
tracing = "0.1.41"
|
||||
tracing-log = "0.2"
|
||||
tracing-opentelemetry = "0.19.0"
|
||||
tracing-subscriber = "0.3.19"
|
||||
tracing-subscriber = "0.3.20"
|
||||
tracing-tree = "0.2.2"
|
||||
tracing-indicatif = "0.3.9"
|
||||
tracing-test = "0.2.5"
|
||||
ts-rs = "10.1.0"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
typed-builder = "0.23.0"
|
||||
uniffi = "0.29.2"
|
||||
uniffi_build = "0.29.0"
|
||||
url = "2.5"
|
||||
@@ -464,6 +454,11 @@ opt-level = 'z'
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
suspicious = "deny"
|
||||
complexity = "deny"
|
||||
perf = "deny"
|
||||
style = "deny"
|
||||
|
||||
unwrap_used = "deny"
|
||||
expect_used = "deny"
|
||||
todo = "deny"
|
||||
|
||||
@@ -107,16 +107,16 @@ sdk-wasm-build:
|
||||
$(MAKE) -C nym-browser-extension/storage wasm-pack
|
||||
$(MAKE) -C wasm/client
|
||||
$(MAKE) -C wasm/node-tester
|
||||
# $(MAKE) -C wasm/mix-fetch
|
||||
$(MAKE) -C wasm/mix-fetch
|
||||
$(MAKE) -C wasm/zknym-lib
|
||||
# $(MAKE) -C wasm/full-nym-wasm
|
||||
|
||||
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
|
||||
sdk-typescript-build:
|
||||
npx lerna run --scope @nymproject/sdk build --stream
|
||||
# npx lerna run --scope @nymproject/mix-fetch build --stream
|
||||
# npx lerna run --scope @nymproject/node-tester build --stream
|
||||
# yarn --cwd sdk/typescript/codegen/contract-clients build
|
||||
npx lerna run --scope @nymproject/mix-fetch build --stream
|
||||
npx lerna run --scope @nymproject/node-tester build --stream
|
||||
yarn --cwd sdk/typescript/codegen/contract-clients build
|
||||
|
||||
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
|
||||
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.68"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.68"
|
||||
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"
|
||||
|
||||
@@ -16,6 +16,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||
semver = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
|
||||
@@ -29,7 +30,13 @@ hmac = { workspace = true, optional = true }
|
||||
sha2 = { workspace = true, optional = true }
|
||||
x25519-dalek = { workspace = true, features = ["static_secrets"] }
|
||||
|
||||
[dev-dependencies]
|
||||
nym-test-utils = { path = "../test-utils" }
|
||||
|
||||
[features]
|
||||
default = ["verify"]
|
||||
# this is moved to a separate feature as we really need clients to import it (especially, *cough*, wasm)
|
||||
verify = ["hmac", "sha2"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -6,9 +6,11 @@ use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
AuthenticatorVersion, Error,
|
||||
latest::registration::IpPair,
|
||||
traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, Versionable},
|
||||
v2, v3, v4, v5,
|
||||
traits::{
|
||||
FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, UpgradeModeMessage,
|
||||
Versionable,
|
||||
},
|
||||
v2, v3, v4, v5, v6,
|
||||
};
|
||||
|
||||
// This is very redundant with AuthenticatorRequest and I reckon they could be smooshed.
|
||||
@@ -19,6 +21,293 @@ pub enum ClientMessage {
|
||||
Final(Box<dyn FinalMessage + Send + Sync + 'static>),
|
||||
Query(Box<dyn QueryBandwidthMessage + Send + Sync + 'static>),
|
||||
TopUp(Box<dyn TopUpMessage + Send + Sync + 'static>),
|
||||
UpgradeModeCheck(Box<dyn UpgradeModeMessage + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
pub struct SerialisedRequest {
|
||||
pub bytes: Vec<u8>,
|
||||
pub request_id: u64,
|
||||
}
|
||||
|
||||
impl SerialisedRequest {
|
||||
pub fn new(bytes: Vec<u8>, request_id: u64) -> Self {
|
||||
Self { bytes, request_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMessage {
|
||||
fn serialise_v1(&self) -> Result<SerialisedRequest, Error> {
|
||||
Err(Error::UnsupportedVersion)
|
||||
}
|
||||
|
||||
fn serialise_v2(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
|
||||
use v2::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ip: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message
|
||||
.credential()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v3(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
|
||||
use v3::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ip: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message
|
||||
.credential()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(
|
||||
TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v4(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
|
||||
use v4::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ips: IpPair {
|
||||
ipv4: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
ipv6: final_message
|
||||
.gateway_client_ipv6()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
},
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message
|
||||
.credential()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(
|
||||
TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v5(&self) -> Result<SerialisedRequest, Error> {
|
||||
use v5::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
});
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ips: IpPair {
|
||||
ipv4: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
ipv6: final_message
|
||||
.gateway_client_ipv6()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
},
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message
|
||||
.credential()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
});
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(query_message.pub_key());
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
});
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v6(&self) -> Result<SerialisedRequest, Error> {
|
||||
use v6::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
upgrade_mode_check::UpgradeModeCheckRequest,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
});
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ips: IpPair {
|
||||
ipv4: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
ipv6: final_message
|
||||
.gateway_client_ipv6()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
},
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
});
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(query_message.pub_key());
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
});
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::UpgradeModeCheck(upgrade_mode_check) => {
|
||||
// currently JWT is the only emergency credential option
|
||||
let Some(upgrade_mode_jwt) =
|
||||
upgrade_mode_check.upgrade_mode_global_attestation_jwt()
|
||||
else {
|
||||
return Err(Error::conversion(
|
||||
"no valid known upgrade mode check variants",
|
||||
));
|
||||
};
|
||||
let msg = UpgradeModeCheckRequest::UpgradeModeJwt {
|
||||
token: upgrade_mode_jwt,
|
||||
};
|
||||
|
||||
let (req, id) = AuthenticatorRequest::new_upgrade_mode_check_request(msg);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMessage {
|
||||
@@ -27,7 +316,7 @@ impl ClientMessage {
|
||||
match self {
|
||||
Self::Final(msg) => msg.credential().is_some(),
|
||||
Self::TopUp(_) => true,
|
||||
Self::Initial(_) | Self::Query(_) => false,
|
||||
Self::Initial(_) | Self::Query(_) | Self::UpgradeModeCheck(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,208 +326,18 @@ impl ClientMessage {
|
||||
ClientMessage::Final(msg) => msg.version(),
|
||||
ClientMessage::Query(msg) => msg.version(),
|
||||
ClientMessage::TopUp(msg) => msg.version(),
|
||||
ClientMessage::UpgradeModeCheck(msg) => msg.version(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes(&self, reply_to: Recipient) -> Result<(Vec<u8>, u64), Error> {
|
||||
pub fn bytes(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
|
||||
match self.version() {
|
||||
AuthenticatorVersion::V1 => Err(Error::UnsupportedVersion),
|
||||
AuthenticatorVersion::V2 => {
|
||||
use v2::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ip: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(
|
||||
query_message.pub_key(),
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::V3 => {
|
||||
use v3::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ip: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(
|
||||
query_message.pub_key(),
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(
|
||||
TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::V4 => {
|
||||
use v4::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ips: IpPair {
|
||||
ipv4: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
ipv6: final_message
|
||||
.gateway_client_ipv6()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
}
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(
|
||||
query_message.pub_key(),
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(
|
||||
TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::V5 => {
|
||||
use v5::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
});
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ips: IpPair {
|
||||
ipv4: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
ipv6: final_message
|
||||
.gateway_client_ipv6()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
},
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
});
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key());
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
});
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::V1 => self.serialise_v1(),
|
||||
AuthenticatorVersion::V2 => self.serialise_v2(reply_to),
|
||||
AuthenticatorVersion::V3 => self.serialise_v3(reply_to),
|
||||
AuthenticatorVersion::V4 => self.serialise_v4(reply_to),
|
||||
AuthenticatorVersion::V5 => self.serialise_v5(),
|
||||
AuthenticatorVersion::V6 => self.serialise_v6(),
|
||||
AuthenticatorVersion::UNKNOWN => Err(Error::UnknownVersion),
|
||||
}
|
||||
}
|
||||
@@ -247,7 +346,7 @@ impl ClientMessage {
|
||||
use AuthenticatorVersion::*;
|
||||
match self.version() {
|
||||
V1 | V2 | V3 | V4 => false,
|
||||
V5 => true,
|
||||
V5 | V6 => true,
|
||||
UNKNOWN => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -37,3 +38,13 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
Bincode(#[from] bincode::Error),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn conversion(msg: impl Into<String>) -> Self {
|
||||
Error::Conversion(msg.into())
|
||||
}
|
||||
|
||||
pub fn conversion_display(msg: impl Display) -> Self {
|
||||
Error::Conversion(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod client_message;
|
||||
pub mod models;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod traits;
|
||||
@@ -10,13 +11,14 @@ pub mod v2;
|
||||
pub mod v3;
|
||||
pub mod v4;
|
||||
pub mod v5;
|
||||
pub mod v6;
|
||||
|
||||
mod error;
|
||||
mod util;
|
||||
mod version;
|
||||
|
||||
pub use error::Error;
|
||||
pub use v5 as latest;
|
||||
pub use v6 as latest;
|
||||
pub use version::AuthenticatorVersion;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = latest::VERSION;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::{
|
||||
BandwidthCredential, CredentialSpendingData, TicketType, UnknownTicketType,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||
pub enum CurrentUpgradeModeStatus {
|
||||
Enabled,
|
||||
Disabled,
|
||||
// everything pre-v6
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl CurrentUpgradeModeStatus {
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
matches!(self, CurrentUpgradeModeStatus::Enabled)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for CurrentUpgradeModeStatus {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
CurrentUpgradeModeStatus::Enabled
|
||||
} else {
|
||||
CurrentUpgradeModeStatus::Disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CurrentUpgradeModeStatus> for Option<bool> {
|
||||
fn from(value: CurrentUpgradeModeStatus) -> Self {
|
||||
match value {
|
||||
CurrentUpgradeModeStatus::Enabled => Some(true),
|
||||
CurrentUpgradeModeStatus::Disabled => Some(false),
|
||||
CurrentUpgradeModeStatus::Unknown => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct BandwidthClaim {
|
||||
pub credential: BandwidthCredential,
|
||||
pub kind: TicketType,
|
||||
}
|
||||
|
||||
impl TryFrom<CredentialSpendingData> for BandwidthClaim {
|
||||
type Error = UnknownTicketType;
|
||||
|
||||
fn try_from(credential: CredentialSpendingData) -> Result<Self, Self::Error> {
|
||||
Ok(BandwidthClaim {
|
||||
kind: TicketType::try_from_encoded(credential.payment.t_type)?,
|
||||
credential: BandwidthCredential::from(credential),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
|
||||
use crate::traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage};
|
||||
use crate::{v1, v2, v3, v4, v5};
|
||||
use crate::traits::{
|
||||
FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, UpgradeModeMessage,
|
||||
};
|
||||
use crate::{v1, v2, v3, v4, v5, v6};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthenticatorRequest {
|
||||
@@ -33,6 +35,11 @@ pub enum AuthenticatorRequest {
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
CheckUpgradeMode {
|
||||
msg: Box<dyn UpgradeModeMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
request_id: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
@@ -202,3 +209,45 @@ impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v6::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v6::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v6::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v6::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v6::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v6::request::AuthenticatorRequestData::CheckUpgradeMode(upgrade_mode_check_msg) => {
|
||||
Self::CheckUpgradeMode {
|
||||
msg: Box::new(upgrade_mode_check_msg),
|
||||
protocol: value.protocol,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::CurrentUpgradeModeStatus;
|
||||
use crate::traits::{
|
||||
Id, PendingRegistrationResponse, RegisteredResponse, RemainingBandwidthResponse,
|
||||
TopUpBandwidthResponse,
|
||||
TopUpBandwidthResponse, UpgradeModeStatus,
|
||||
};
|
||||
use crate::{v2, v3, v4, v5};
|
||||
use crate::{v2, v3, v4, v5, v6};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthenticatorResponse {
|
||||
@@ -13,6 +14,29 @@ pub enum AuthenticatorResponse {
|
||||
Registered(Box<dyn RegisteredResponse + Send + Sync + 'static>),
|
||||
RemainingBandwidth(Box<dyn RemainingBandwidthResponse + Send + Sync + 'static>),
|
||||
TopUpBandwidth(Box<dyn TopUpBandwidthResponse + Send + Sync + 'static>),
|
||||
UpgradeMode(Box<dyn UpgradeModeStatus + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for AuthenticatorResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
match self {
|
||||
AuthenticatorResponse::PendingRegistration(pending_registration_response) => {
|
||||
pending_registration_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::Registered(registered_response) => {
|
||||
registered_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::RemainingBandwidth(remaining_bandwidth_response) => {
|
||||
remaining_bandwidth_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
top_up_bandwidth_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::UpgradeMode(upgrade_mode_response) => {
|
||||
upgrade_mode_response.upgrade_mode_status()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for AuthenticatorResponse {
|
||||
@@ -28,6 +52,7 @@ impl Id for AuthenticatorResponse {
|
||||
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
top_up_bandwidth_response.id()
|
||||
}
|
||||
AuthenticatorResponse::UpgradeMode(upgrade_mode_response) => upgrade_mode_response.id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,3 +129,25 @@ impl From<v5::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
fn from(value: v6::response::AuthenticatorResponse) -> Self {
|
||||
match value.data {
|
||||
v6::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => Self::PendingRegistration(Box::new(pending_registration_response)),
|
||||
v6::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
Self::Registered(Box::new(registered_response))
|
||||
}
|
||||
v6::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
|
||||
v6::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
|
||||
}
|
||||
v6::response::AuthenticatorResponseData::UpgradeMode(upgrade_mode_check_response) => {
|
||||
Self::UpgradeMode(Box::new(upgrade_mode_check_response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::latest::registration::IpPair;
|
||||
use crate::models::{BandwidthClaim, CurrentUpgradeModeStatus};
|
||||
use crate::{AuthenticatorVersion, Error, v1, v2, v3, v4, v5, v6};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use std::fmt;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519::PrivateKey;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::latest::registration::IpPair;
|
||||
use crate::{AuthenticatorVersion, Error, v1, v2, v3, v4, v5};
|
||||
use tracing::error;
|
||||
|
||||
pub trait Versionable {
|
||||
fn version(&self) -> AuthenticatorVersion;
|
||||
@@ -51,6 +51,12 @@ impl Versionable for v5::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v6::registration::InitMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v2::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V2
|
||||
@@ -75,6 +81,12 @@ impl Versionable for v5::registration::FinalMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v6::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for PeerPublicKey {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
@@ -98,6 +110,158 @@ impl Versionable for v5::topup::TopUpMessage {
|
||||
AuthenticatorVersion::V5
|
||||
}
|
||||
}
|
||||
impl Versionable for v6::topup::TopUpMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v6::upgrade_mode_check::UpgradeModeCheckRequest {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpgradeModeStatus: Id + fmt::Debug {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus;
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v1::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v1::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v1::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v2::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v2::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v2::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
impl UpgradeModeStatus for v3::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v3::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v3::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v3::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::UpgradeModeResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitMessage: Versionable + fmt::Debug {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
@@ -133,14 +297,20 @@ impl InitMessage for v5::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v6::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FinalMessage: Versionable + fmt::Debug {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey;
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr>;
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr>;
|
||||
fn gateway_client_mac(&self) -> Vec<u8>;
|
||||
fn credential(&self) -> Option<CredentialSpendingData>;
|
||||
fn credential(&self) -> Option<BandwidthClaim>;
|
||||
}
|
||||
|
||||
impl FinalMessage for v1::GatewayClient {
|
||||
@@ -148,7 +318,7 @@ impl FinalMessage for v1::GatewayClient {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -171,7 +341,7 @@ impl FinalMessage for v1::GatewayClient {
|
||||
self.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -181,7 +351,7 @@ impl FinalMessage for v2::registration::FinalMessage {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -204,8 +374,12 @@ impl FinalMessage for v2::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +388,7 @@ impl FinalMessage for v3::registration::FinalMessage {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -237,8 +411,12 @@ impl FinalMessage for v3::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +425,42 @@ impl FinalMessage for v4::registration::FinalMessage {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
// v4 -> v5 -> v6
|
||||
v5::registration::IpPair::from(self.gateway_client.private_ips).into()
|
||||
}
|
||||
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
Some(self.gateway_client.private_ips.ipv4)
|
||||
}
|
||||
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
Some(self.gateway_client.private_ips.ipv6)
|
||||
}
|
||||
|
||||
fn gateway_client_mac(&self) -> Vec<u8> {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v5::registration::FinalMessage {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -267,17 +480,21 @@ impl FinalMessage for v4::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v5::registration::FinalMessage {
|
||||
impl FinalMessage for v6::registration::FinalMessage {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -297,7 +514,7 @@ impl FinalMessage for v5::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
@@ -347,10 +564,42 @@ impl TopUpMessage for v5::topup::TopUpMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpMessage for v6::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpgradeModeMessage: Versionable + fmt::Debug {
|
||||
// the idea is to expose different types of emergency credentials here,
|
||||
// like upgrade mode JWT, emergency threshold credential issued by signers, etc.
|
||||
fn upgrade_mode_global_attestation_jwt(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
impl UpgradeModeMessage for v6::upgrade_mode_check::UpgradeModeCheckRequest {
|
||||
fn upgrade_mode_global_attestation_jwt(&self) -> Option<String> {
|
||||
use v6::upgrade_mode_check::UpgradeModeCheckRequest;
|
||||
|
||||
match self {
|
||||
UpgradeModeCheckRequest::UpgradeModeJwt { token } => Some(token.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Id {
|
||||
fn id(&self) -> u64;
|
||||
}
|
||||
|
||||
impl Id for v1::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v2::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -375,6 +624,18 @@ impl Id for v5::response::PendingRegistrationResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v1::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v2::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -399,6 +660,18 @@ impl Id for v5::response::RegisteredResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v1::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v2::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -423,6 +696,12 @@ impl Id for v5::response::RemainingBandwidthResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v3::response::TopUpBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -441,11 +720,28 @@ impl Id for v5::response::TopUpBandwidthResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PendingRegistrationResponse: Id + fmt::Debug {
|
||||
impl Id for v6::response::TopUpBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::UpgradeModeResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PendingRegistrationResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn nonce(&self) -> u64;
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error>;
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error>;
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync>;
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
|
||||
@@ -453,7 +749,7 @@ impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -464,6 +760,22 @@ impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ip.into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v2::registration::FinalMessage {
|
||||
gateway_client: v2::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.private_ips().ipv4.into(),
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
|
||||
@@ -471,7 +783,7 @@ impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -482,6 +794,22 @@ impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ip.into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v3::registration::FinalMessage {
|
||||
gateway_client: v3::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.private_ips().ipv4.into(),
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
|
||||
@@ -489,7 +817,42 @@ impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.gateway_data.pub_key
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
// v4 -> v5 -> v6
|
||||
v5::registration::IpPair::from(self.reply.gateway_data.private_ips).into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v4::registration::FinalMessage {
|
||||
gateway_client: v4::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.reply.gateway_data.private_ips,
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -500,14 +863,30 @@ impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ips.into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v5::registration::FinalMessage {
|
||||
gateway_client: v5::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.reply.gateway_data.private_ips,
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
|
||||
impl PendingRegistrationResponse for v6::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -518,9 +897,25 @@ impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ips
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v6::registration::FinalMessage {
|
||||
gateway_client: v6::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.reply.gateway_data.private_ips,
|
||||
self.nonce(),
|
||||
),
|
||||
credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RegisteredResponse: Id + fmt::Debug {
|
||||
pub trait RegisteredResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn wg_port(&self) -> u16;
|
||||
@@ -555,7 +950,8 @@ impl RegisteredResponse for v3::response::RegisteredResponse {
|
||||
}
|
||||
impl RegisteredResponse for v4::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ips.into()
|
||||
// v4 -> v5 -> v6
|
||||
v5::registration::IpPair::from(self.reply.private_ips).into()
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
@@ -568,6 +964,20 @@ impl RegisteredResponse for v4::response::RegisteredResponse {
|
||||
}
|
||||
|
||||
impl RegisteredResponse for v5::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ips.into()
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.pub_key
|
||||
}
|
||||
|
||||
fn wg_port(&self) -> u16 {
|
||||
self.reply.wg_port
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisteredResponse for v6::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ips
|
||||
}
|
||||
@@ -581,7 +991,7 @@ impl RegisteredResponse for v5::response::RegisteredResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RemainingBandwidthResponse: Id + fmt::Debug {
|
||||
pub trait RemainingBandwidthResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn available_bandwidth(&self) -> Option<i64>;
|
||||
}
|
||||
|
||||
@@ -609,7 +1019,13 @@ impl RemainingBandwidthResponse for v5::response::RemainingBandwidthResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpBandwidthResponse: Id + fmt::Debug {
|
||||
impl RemainingBandwidthResponse for v6::response::RemainingBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> Option<i64> {
|
||||
self.reply.as_ref().map(|r| r.available_bandwidth)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpBandwidthResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn available_bandwidth(&self) -> i64;
|
||||
}
|
||||
|
||||
@@ -630,3 +1046,9 @@ impl TopUpBandwidthResponse for v5::response::TopUpBandwidthResponse {
|
||||
self.reply.available_bandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpBandwidthResponse for v6::response::TopUpBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> i64 {
|
||||
self.reply.available_bandwidth
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -34,7 +34,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -108,7 +108,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -154,8 +154,8 @@ impl From<v2::registration::RegistrationData> for v1::registration::Registration
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistredData> for v1::registration::RegistredData {
|
||||
fn from(value: v2::registration::RegistredData) -> Self {
|
||||
impl From<v2::registration::RegisteredData> for v1::registration::RegisteredData {
|
||||
fn from(value: v2::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
|
||||
@@ -58,7 +58,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -118,7 +118,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -299,8 +299,8 @@ impl From<v2::registration::RegistrationData> for v3::registration::Registration
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
|
||||
fn from(value: v3::registration::RegistredData) -> Self {
|
||||
impl From<v3::registration::RegisteredData> for v2::registration::RegisteredData {
|
||||
fn from(value: v3::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
@@ -309,8 +309,8 @@ impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistredData> for v3::registration::RegistredData {
|
||||
fn from(value: v2::registration::RegistredData) -> Self {
|
||||
impl From<v2::registration::RegisteredData> for v3::registration::RegisteredData {
|
||||
fn from(value: v2::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
@@ -674,7 +674,7 @@ mod tests {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let wg_port = 51822;
|
||||
let registred_data = v2::registration::RegistredData {
|
||||
let registred_data = v2::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ip,
|
||||
wg_port,
|
||||
@@ -701,7 +701,7 @@ mod tests {
|
||||
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v3::registration::RegistredData {
|
||||
reply: v3::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip
|
||||
@@ -715,7 +715,7 @@ mod tests {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let wg_port = 51822;
|
||||
let registred_data = v3::registration::RegistredData {
|
||||
let registred_data = v3::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ip,
|
||||
wg_port,
|
||||
@@ -742,7 +742,7 @@ mod tests {
|
||||
v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v2::registration::RegistredData {
|
||||
reply: v2::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip
|
||||
|
||||
@@ -58,7 +58,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -139,7 +139,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -262,8 +262,8 @@ impl From<v4::response::TopUpBandwidthResponse> for v3::response::TopUpBandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
|
||||
fn from(value: v3::registration::RegistredData) -> Self {
|
||||
impl From<v3::registration::RegisteredData> for v4::registration::RegisteredData {
|
||||
fn from(value: v3::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ip.into(),
|
||||
@@ -272,8 +272,8 @@ impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistredData> for v3::registration::RegistredData {
|
||||
fn from(value: v4::registration::RegistredData) -> Self {
|
||||
impl From<v4::registration::RegisteredData> for v3::registration::RegisteredData {
|
||||
fn from(value: v4::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ips.ipv4.into(),
|
||||
@@ -565,7 +565,7 @@ mod tests {
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap());
|
||||
let wg_port = 51822;
|
||||
let registred_data = v3::registration::RegistredData {
|
||||
let registred_data = v3::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ip: ipv4.into(),
|
||||
wg_port,
|
||||
@@ -592,7 +592,7 @@ mod tests {
|
||||
v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v4::registration::RegistredData {
|
||||
reply: v4::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips
|
||||
@@ -608,7 +608,7 @@ mod tests {
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
|
||||
let wg_port = 51822;
|
||||
let registred_data = v4::registration::RegistredData {
|
||||
let registred_data = v4::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
@@ -635,7 +635,7 @@ mod tests {
|
||||
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v3::registration::RegistredData {
|
||||
reply: v3::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip: ipv4.into()
|
||||
|
||||
@@ -110,7 +110,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -139,7 +139,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -186,8 +186,8 @@ impl From<v4::response::TopUpBandwidthResponse> for v5::response::TopUpBandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistredData> for v5::registration::RegistredData {
|
||||
fn from(value: v4::registration::RegistredData) -> Self {
|
||||
impl From<v4::registration::RegisteredData> for v5::registration::RegisteredData {
|
||||
fn from(value: v4::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ips.into(),
|
||||
@@ -405,7 +405,7 @@ mod tests {
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let private_ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let wg_port = 51822;
|
||||
let registred_data = v4::registration::RegistredData {
|
||||
let registred_data = v4::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
@@ -431,7 +431,7 @@ mod tests {
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::Registered(v5::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply: v5::registration::RegistredData {
|
||||
reply: v5::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips: v5::registration::IpPair::new(ipv4, ipv6)
|
||||
|
||||
@@ -108,7 +108,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -32,7 +32,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_registered(registred_data: RegistredData, request_id: u64) -> Self {
|
||||
pub fn new_registered(registred_data: RegisteredData, request_id: u64) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
@@ -116,7 +116,7 @@ pub struct PendingRegistrationResponse {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{v5, v6};
|
||||
|
||||
impl TryFrom<v5::request::AuthenticatorRequest> for v6::request::AuthenticatorRequest {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_request: v5::request::AuthenticatorRequest,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
protocol: v6::PROTOCOL,
|
||||
data: authenticator_request.data.try_into()?,
|
||||
request_id: authenticator_request.request_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v5::request::AuthenticatorRequestData> for v6::request::AuthenticatorRequestData {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_request_data: v5::request::AuthenticatorRequestData,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match authenticator_request_data {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
|
||||
v6::request::AuthenticatorRequestData::Initial(init_msg.into()),
|
||||
),
|
||||
v5::request::AuthenticatorRequestData::Final(final_msg) => Ok(
|
||||
v6::request::AuthenticatorRequestData::Final(Box::new((*final_msg).try_into()?)),
|
||||
),
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
|
||||
v6::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
|
||||
),
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
|
||||
v6::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::InitMessage> for v6::registration::InitMessage {
|
||||
fn from(init_msg: v5::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
pub_key: init_msg.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v5::registration::FinalMessage> for v6::registration::FinalMessage {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(final_msg: v5::registration::FinalMessage) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
gateway_client: final_msg.gateway_client.into(),
|
||||
credential: final_msg
|
||||
.credential
|
||||
.map(TryInto::try_into)
|
||||
.transpose()
|
||||
.map_err(Self::Error::conversion_display)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::GatewayClient> for v6::registration::GatewayClient {
|
||||
fn from(gateway_client: v5::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::GatewayClient> for v5::registration::GatewayClient {
|
||||
fn from(gateway_client: v6::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::ClientMac> for v6::registration::ClientMac {
|
||||
fn from(client_mac: v5::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::ClientMac> for v5::registration::ClientMac {
|
||||
fn from(client_mac: v6::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v5::topup::TopUpMessage>> for Box<v6::topup::TopUpMessage> {
|
||||
fn from(top_up_message: Box<v5::topup::TopUpMessage>) -> Self {
|
||||
Box::new(v6::topup::TopUpMessage {
|
||||
pub_key: top_up_message.pub_key,
|
||||
credential: top_up_message.credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::AuthenticatorResponse> for v6::response::AuthenticatorResponse {
|
||||
fn from(value: v5::response::AuthenticatorResponse) -> Self {
|
||||
Self {
|
||||
protocol: v6::PROTOCOL,
|
||||
data: value.data.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::AuthenticatorResponseData> for v6::response::AuthenticatorResponseData {
|
||||
fn from(authenticator_response_data: v5::response::AuthenticatorResponseData) -> Self {
|
||||
match authenticator_response_data {
|
||||
v5::response::AuthenticatorResponseData::PendingRegistration(pending_response) => {
|
||||
v6::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_response.into(),
|
||||
)
|
||||
}
|
||||
v5::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
v6::response::AuthenticatorResponseData::Registered(registered_response.into())
|
||||
}
|
||||
v5::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => v6::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response.into(),
|
||||
),
|
||||
v5::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => {
|
||||
v6::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::RegisteredResponse> for v6::response::RegisteredResponse {
|
||||
fn from(value: v5::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::PendingRegistrationResponse> for v6::response::PendingRegistrationResponse {
|
||||
fn from(value: v5::response::PendingRegistrationResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RegistrationData> for v6::registration::RegistrationData {
|
||||
fn from(value: v5::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::RegistrationData> for v5::registration::RegistrationData {
|
||||
fn from(value: v6::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::RemainingBandwidthResponse> for v6::response::RemainingBandwidthResponse {
|
||||
fn from(value: v5::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.map(Into::into),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::TopUpBandwidthResponse> for v6::response::TopUpBandwidthResponse {
|
||||
fn from(value: v5::response::TopUpBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RegisteredData> for v6::registration::RegisteredData {
|
||||
fn from(value: v5::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ips.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RemainingBandwidthData> for v6::registration::RemainingBandwidthData {
|
||||
fn from(value: v5::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::IpPair> for v6::registration::IpPair {
|
||||
fn from(value: v5::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::IpPair> for v5::registration::IpPair {
|
||||
fn from(value: v6::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use nym_credentials_interface::{BandwidthCredential, CredentialSpendingData, TicketType};
|
||||
use nym_crypto::asymmetric::x25519::PrivateKey;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use super::*;
|
||||
use crate::models::BandwidthClaim;
|
||||
use crate::{util::tests::CREDENTIAL_BYTES, v5};
|
||||
|
||||
#[test]
|
||||
fn upgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
|
||||
let (msg, _) = v5::request::AuthenticatorRequest::new_initial_request(
|
||||
v5::registration::InitMessage::new(pub_key),
|
||||
);
|
||||
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::request::AuthenticatorRequestData::Initial(v6::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v5::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let gateway_client = v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
|
||||
let final_message = v5::registration::FinalMessage {
|
||||
gateway_client: gateway_client.clone(),
|
||||
credential: Some(credential.clone()),
|
||||
};
|
||||
|
||||
let (msg, _) = v5::request::AuthenticatorRequest::new_final_request(final_message);
|
||||
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::request::AuthenticatorRequestData::Final(Box::new(
|
||||
v6::registration::FinalMessage {
|
||||
gateway_client: v6::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v6::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
credential: Some(BandwidthClaim {
|
||||
credential: BandwidthCredential::ZkNym(Box::new(credential)),
|
||||
kind: TicketType::V1MixnetEntry,
|
||||
})
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
|
||||
let (msg, _) = v5::request::AuthenticatorRequest::new_query_request(pub_key);
|
||||
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v5::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v5::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
|
||||
let msg = v5::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::response::AuthenticatorResponseData::PendingRegistration(
|
||||
v6::response::PendingRegistrationResponse {
|
||||
request_id,
|
||||
reply: v6::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data: v6::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v6::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
wg_port
|
||||
},
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let private_ips = v5::registration::IpPair::new(ipv4, ipv6);
|
||||
let wg_port = 51822;
|
||||
let registered_data = v5::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
|
||||
let msg = v5::response::AuthenticatorResponse::new_registered(registered_data, request_id);
|
||||
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::response::AuthenticatorResponseData::Registered(v6::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply: v6::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips: v6::registration::IpPair::new(ipv4, ipv6)
|
||||
},
|
||||
upgrade_mode_enabled: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v5::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
|
||||
let msg = v5::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v6::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply: Some(v6::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
}),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
pub mod conversion;
|
||||
pub mod registration;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod topup;
|
||||
pub mod upgrade_mode_check;
|
||||
|
||||
pub const VERSION: u8 = 6;
|
||||
|
||||
pub const PROTOCOL: Protocol = Protocol::new(VERSION, ServiceProviderType::Authenticator);
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::models::BandwidthClaim;
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::time::SystemTime;
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
use hmac::{Hmac, Mac};
|
||||
#[cfg(feature = "verify")]
|
||||
use nym_crypto::asymmetric::x25519::{PrivateKey, PublicKey};
|
||||
#[cfg(feature = "verify")]
|
||||
use sha2::Sha256;
|
||||
|
||||
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
|
||||
pub type PrivateIPs = HashMap<IpPair, Taken>;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct IpPair {
|
||||
pub ipv4: Ipv4Addr,
|
||||
pub ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
impl IpPair {
|
||||
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
|
||||
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IpPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.ipv4, self.ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IpAddr> for IpPair {
|
||||
fn from(value: IpAddr) -> Self {
|
||||
let (before_last_byte, last_byte) = match value {
|
||||
IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]),
|
||||
IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]),
|
||||
};
|
||||
let last_bytes = ((before_last_byte as u16) << 8) | last_byte as u16;
|
||||
let ipv4 = Ipv4Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1],
|
||||
before_last_byte,
|
||||
last_byte,
|
||||
);
|
||||
let ipv6 = Ipv6Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[1],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[2],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[3],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[4],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[5],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[6],
|
||||
last_bytes,
|
||||
);
|
||||
IpPair::new(ipv4, ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InitMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
}
|
||||
|
||||
impl InitMessage {
|
||||
pub fn new(pub_key: PeerPublicKey) -> Self {
|
||||
InitMessage { pub_key }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct FinalMessage {
|
||||
/// Gateway client data
|
||||
pub gateway_client: GatewayClient,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: Option<BandwidthClaim>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistrationData {
|
||||
pub nonce: u64,
|
||||
pub gateway_data: GatewayClient,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
/// Gateway/Nym node can then verify pub_key payload using the same process
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct GatewayClient {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Assigned private IPs (v4 and v6)
|
||||
pub private_ips: IpPair,
|
||||
|
||||
/// Sha256 hmac on the data (alongside the prior nonce)
|
||||
pub mac: ClientMac,
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn new(
|
||||
local_secret: &PrivateKey,
|
||||
remote_public: x25519_dalek::PublicKey,
|
||||
private_ips: IpPair,
|
||||
nonce: u64,
|
||||
) -> Self {
|
||||
let local_public = PublicKey::from(local_secret);
|
||||
let remote_public = PublicKey::from(remote_public);
|
||||
|
||||
let dh = local_secret.diffie_hellman(&remote_public);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(&dh[..])
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(local_public.as_bytes());
|
||||
mac.update(private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
GatewayClient {
|
||||
pub_key: PeerPublicKey::new(local_public.into()),
|
||||
private_ips,
|
||||
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable secret should be gateways Wireguard PK
|
||||
// Client should perform this step when generating its payload, using its own WG PK
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
// use gateways key as a ref to an x25519_dalek key
|
||||
let dh = gateway_key.inner().diffie_hellman(&self.pub_key);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
mac.verify_slice(&self.mac)
|
||||
.map_err(|source| Error::FailedClientMacVerification {
|
||||
client: self.pub_key.to_string(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
|
||||
// TODO2: rely on our internal crypto/hmac
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ClientMac(Vec<u8>);
|
||||
|
||||
impl fmt::Display for ClientMac {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", general_purpose::STANDARD.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ClientMac {
|
||||
fn from(v: Vec<u8>) -> Self {
|
||||
ClientMac(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMac {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(mac: Vec<u8>) -> Self {
|
||||
ClientMac(mac)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientMac {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientMac {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mac_bytes: Vec<u8> =
|
||||
general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|source| Error::MalformedClientMac {
|
||||
mac: s.to_string(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
Ok(ClientMac(mac_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientMac {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMac {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_test_utils::helpers::deterministic_rng;
|
||||
|
||||
#[test]
|
||||
fn create_ip_pair() {
|
||||
let ipv4: IpAddr = Ipv4Addr::from_str("10.1.10.50").unwrap().into();
|
||||
let ipv6: IpAddr = Ipv6Addr::from_str("fc01::0a32").unwrap().into();
|
||||
|
||||
assert_eq!(IpPair::from(ipv4), IpPair::from(ipv6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "verify")]
|
||||
fn client_request_roundtrip() {
|
||||
let mut rng = deterministic_rng();
|
||||
|
||||
let gateway_key_pair = x25519::KeyPair::new(&mut rng);
|
||||
let client_key_pair = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let nonce = 1234567890;
|
||||
|
||||
let client = GatewayClient::new(
|
||||
client_key_pair.private_key(),
|
||||
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
|
||||
IpPair::new("10.0.0.42".parse().unwrap(), "fc00::42".parse().unwrap()),
|
||||
nonce,
|
||||
);
|
||||
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{
|
||||
PROTOCOL,
|
||||
registration::{FinalMessage, InitMessage},
|
||||
topup::TopUpMessage,
|
||||
upgrade_mode_check::UpgradeModeCheckRequest,
|
||||
};
|
||||
use nym_service_provider_requests_common::Protocol;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorRequest {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorRequestData,
|
||||
pub request_id: u64,
|
||||
}
|
||||
|
||||
impl AuthenticatorRequest {
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn new_initial_request(init_message: InitMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::Initial(init_message),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_final_request(final_message: FinalMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::Final(Box::new(final_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_query_request(peer_public_key: PeerPublicKey) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_topup_request(top_up_message: TopUpMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::TopUpBandwidth(Box::new(top_up_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_upgrade_mode_check_request(message: UpgradeModeCheckRequest) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::CheckUpgradeMode(message),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorRequestData {
|
||||
Initial(InitMessage),
|
||||
Final(Box<FinalMessage>),
|
||||
QueryBandwidth(PeerPublicKey),
|
||||
TopUpBandwidth(Box<TopUpMessage>),
|
||||
CheckUpgradeMode(UpgradeModeCheckRequest),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::VERSION;
|
||||
use super::*;
|
||||
use nym_service_provider_requests_common::ServiceProviderType;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn check_first_bytes_protocol() {
|
||||
let version = VERSION;
|
||||
let data = AuthenticatorRequest {
|
||||
protocol: Protocol {
|
||||
version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: AuthenticatorRequestData::Initial(InitMessage::new(
|
||||
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
|
||||
)),
|
||||
request_id: 1,
|
||||
};
|
||||
let bytes = *data.to_bytes().unwrap().first_chunk::<2>().unwrap();
|
||||
assert_eq!(bytes, [version, ServiceProviderType::Authenticator as u8]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::Protocol;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::PROTOCOL;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorResponse {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorResponseData,
|
||||
}
|
||||
|
||||
impl AuthenticatorResponse {
|
||||
pub fn new_pending_registration_success(
|
||||
registration_data: RegistrationData,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::PendingRegistration(PendingRegistrationResponse {
|
||||
reply: registration_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registered_data: RegisteredData,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::Registered(RegisteredResponse {
|
||||
reply: registered_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_remaining_bandwidth(
|
||||
remaining_bandwidth_data: Option<RemainingBandwidthData>,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_topup_bandwidth(
|
||||
remaining_bandwidth_data: RemainingBandwidthData,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::TopUpBandwidth(TopUpBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_upgrade_mode_check(request_id: u64, upgrade_mode_enabled: bool) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::UpgradeMode(UpgradeModeResponse {
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::TopUpBandwidth(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::UpgradeMode(response) => Some(response.request_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorResponseData {
|
||||
PendingRegistration(PendingRegistrationResponse),
|
||||
Registered(RegisteredResponse),
|
||||
RemainingBandwidth(RemainingBandwidthResponse),
|
||||
TopUpBandwidth(TopUpBandwidthResponse),
|
||||
UpgradeMode(UpgradeModeResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingRegistrationResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistrationData,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegisteredData,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RemainingBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: Option<RemainingBandwidthData>,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TopUpBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RemainingBandwidthData,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct UpgradeModeResponse {
|
||||
pub request_id: u64,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct TopUpMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: CredentialSpendingData,
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum UpgradeModeCheckRequest {
|
||||
/// Attempt to request upgrade mode recheck via the JWT issued as the result of
|
||||
/// global attestation.json being published
|
||||
UpgradeModeJwt { token: String },
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{v1, v2, v3, v4, v5};
|
||||
use super::{v1, v2, v3, v4, v5, v6};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::Display)]
|
||||
@@ -22,11 +22,15 @@ pub enum AuthenticatorVersion {
|
||||
/// introduced in dorina-patched release (1.6.1)
|
||||
V5,
|
||||
|
||||
/// introduced in niolo release (1.23.0)
|
||||
V6,
|
||||
|
||||
/// an unknown, future, variant that can be present if running outdated software
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
impl AuthenticatorVersion {
|
||||
pub const LATEST: Self = Self::V5;
|
||||
pub const LATEST: Self = Self::V6;
|
||||
|
||||
pub const fn release_version(&self) -> semver::Version {
|
||||
match self {
|
||||
@@ -35,6 +39,7 @@ impl AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3 => semver::Version::new(1, 1, 10),
|
||||
AuthenticatorVersion::V4 => semver::Version::new(1, 2, 0),
|
||||
AuthenticatorVersion::V5 => semver::Version::new(1, 6, 1),
|
||||
AuthenticatorVersion::V6 => semver::Version::new(1, 23, 0),
|
||||
AuthenticatorVersion::UNKNOWN => semver::Version::new(0, 0, 0),
|
||||
}
|
||||
}
|
||||
@@ -54,6 +59,8 @@ impl From<Protocol> for AuthenticatorVersion {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value.version == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else if value.version == v6::VERSION {
|
||||
AuthenticatorVersion::V6
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
@@ -72,6 +79,8 @@ impl From<u8> for AuthenticatorVersion {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else if value == v6::VERSION {
|
||||
AuthenticatorVersion::V6
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
@@ -126,11 +135,14 @@ impl From<semver::Version> for AuthenticatorVersion {
|
||||
if semver < AuthenticatorVersion::V5.release_version() {
|
||||
return Self::V4;
|
||||
}
|
||||
// if provided version is higher (or equal) to release version of V5,
|
||||
// we return the latest (i.e. v5)
|
||||
if semver < AuthenticatorVersion::V6.release_version() {
|
||||
return Self::V5;
|
||||
}
|
||||
// if provided version is higher (or equal) to release version of V6,
|
||||
// we return the latest (i.e. v6)
|
||||
|
||||
debug_assert_eq!(
|
||||
Self::V5,
|
||||
Self::V6,
|
||||
Self::LATEST,
|
||||
"a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait"
|
||||
);
|
||||
@@ -191,5 +203,9 @@ mod tests {
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.7.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.16.11".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.17.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.22.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V6, "1.23.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V6, "1.23.1".into());
|
||||
assert_eq!(AuthenticatorVersion::V6, "1.24.0".into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,16 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "stream_cipher", "aes", "hashing"] }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-task = { path = "../task" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ pub enum BandwidthControllerError {
|
||||
#[error("There was a credential storage error - {0}")]
|
||||
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
|
||||
|
||||
#[error("retrieved upgrade mode token is not a valid String")]
|
||||
MalformedUpgradeModeToken,
|
||||
|
||||
#[error("the credential storage does not contain any usable credentials")]
|
||||
NoCredentialsAvailable,
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::utils::{
|
||||
ApiClientsWrapper,
|
||||
};
|
||||
use log::error;
|
||||
use nym_credential_storage::models::RetrievedTicketbook;
|
||||
use nym_credential_storage::models::{EmergencyCredential, RetrievedTicketbook};
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::ecash::bandwidth::CredentialSpendingData;
|
||||
use nym_credentials_interface::{
|
||||
@@ -220,6 +220,19 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_emergency_credential(
|
||||
&self,
|
||||
typ: &str,
|
||||
) -> Result<Option<EmergencyCredential>, BandwidthControllerError>
|
||||
where
|
||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
self.storage
|
||||
.get_emergency_credential(typ)
|
||||
.await
|
||||
.map_err(BandwidthControllerError::credential_storage_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, St> Clone for BandwidthController<C, St>
|
||||
|
||||
@@ -11,6 +11,9 @@ use crate::{error::BandwidthControllerError, BandwidthController, PreparedCreden
|
||||
|
||||
pub const DEFAULT_TICKETS_TO_SPEND: u32 = 1;
|
||||
|
||||
// TODO: this does not really belong here
|
||||
pub const UPGRADE_MODE_JWT_TYPE: &str = "UPGRADE_MODE_JWT";
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait BandwidthTicketProvider: Send + Sync {
|
||||
@@ -20,6 +23,8 @@ pub trait BandwidthTicketProvider: Send + Sync {
|
||||
gateway_id: ed25519::PublicKey,
|
||||
tickets_to_spend: u32,
|
||||
) -> Result<PreparedCredential, BandwidthControllerError>;
|
||||
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError>;
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -39,4 +44,16 @@ where
|
||||
self.prepare_ecash_ticket(ticket_type, gateway_id.to_bytes(), tickets_to_spend)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
|
||||
let Some(emergency_credential) =
|
||||
self.get_emergency_credential(UPGRADE_MODE_JWT_TYPE).await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
// upgrade mode credential is just a simple stringified JWT
|
||||
let token = String::from_utf8(emergency_credential.data.content)
|
||||
.map_err(|_| BandwidthControllerError::MalformedUpgradeModeToken)?;
|
||||
Ok(Some(token))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nym-bandwidth-controller = { path = "../bandwidth-controller" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-http-api-client = { path = "../http-api-client", features = ["network-defaults"] }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-statistics-common = { path = "../statistics" }
|
||||
|
||||
@@ -81,6 +81,10 @@ pub struct CommonClientInitArgs {
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Change the default minimum node performance used during initial node selection filtering.
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub minimum_gateway_performance: Option<u8>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
@@ -173,10 +177,14 @@ where
|
||||
})?;
|
||||
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||
} else {
|
||||
let minimum_performance = common_args
|
||||
.minimum_gateway_performance
|
||||
.unwrap_or(core.debug.topology.minimum_gateway_performance);
|
||||
|
||||
crate::init::helpers::gateways_for_init(
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
minimum_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
None,
|
||||
)
|
||||
|
||||
@@ -73,6 +73,10 @@ use url::Url;
|
||||
#[cfg(debug_assertions)]
|
||||
use wasm_utils::console_log;
|
||||
|
||||
/// Default number of retries for Nym API requests when using network details with domain fronting.
|
||||
/// This allows the client to try alternative URLs if the primary endpoint is unavailable.
|
||||
const DEFAULT_NYM_API_RETRIES: usize = 3;
|
||||
|
||||
#[cfg(all(
|
||||
not(target_arch = "wasm32"),
|
||||
feature = "fs-surb-storage",
|
||||
@@ -212,6 +216,9 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
|
||||
// Optional API URLs for domain fronting support
|
||||
nym_api_urls: Option<Vec<nym_network_defaults::ApiUrl>>,
|
||||
|
||||
wait_for_gateway: bool,
|
||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
|
||||
@@ -241,6 +248,7 @@ where
|
||||
config: base_config,
|
||||
client_store,
|
||||
dkg_query_client,
|
||||
nym_api_urls: None,
|
||||
wait_for_gateway: false,
|
||||
custom_topology_provider: None,
|
||||
custom_gateway_transceiver: None,
|
||||
@@ -263,6 +271,16 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Set Nym API URLs for domain fronting support.
|
||||
///
|
||||
/// When provided, the client will use these API URLs (which include front_hosts)
|
||||
/// to construct HTTP clients with domain fronting enabled.
|
||||
#[must_use]
|
||||
pub fn with_nym_api_urls(mut self, nym_api_urls: Vec<nym_network_defaults::ApiUrl>) -> Self {
|
||||
self.nym_api_urls = Some(nym_api_urls);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
|
||||
self.config.debug.forget_me = *forget_me;
|
||||
@@ -783,7 +801,7 @@ where
|
||||
event_tx,
|
||||
);
|
||||
|
||||
let mix_tx = mix_traffic_controller.mix_rx();
|
||||
let mix_tx = mix_traffic_controller.mix_tx();
|
||||
let client_tx = mix_traffic_controller.client_tx();
|
||||
|
||||
shutdown_tracker.try_spawn_named(
|
||||
@@ -863,21 +881,67 @@ where
|
||||
}
|
||||
|
||||
fn construct_nym_api_client(
|
||||
nym_api_urls: Option<&Vec<nym_network_defaults::ApiUrl>>,
|
||||
config: &Config,
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Result<nym_http_api_client::Client, ClientCoreError> {
|
||||
tracing::debug!(
|
||||
"construct_nym_api_client called with nym_api_urls: {}",
|
||||
nym_api_urls.is_some()
|
||||
);
|
||||
|
||||
// If API URLs are provided, use new_with_fronted_urls() which handles domain fronting
|
||||
if let Some(nym_api_urls) = nym_api_urls {
|
||||
if nym_api_urls.is_empty() {
|
||||
tracing::warn!("Provided nym_api_urls is empty, falling back to config endpoints");
|
||||
} else {
|
||||
tracing::info!(
|
||||
"Building nym-api client from provided URLs (with domain fronting support): {} URLs",
|
||||
nym_api_urls.len()
|
||||
);
|
||||
|
||||
let mut builder =
|
||||
nym_http_api_client::ClientBuilder::new_with_fronted_urls(nym_api_urls.clone())
|
||||
.map_err(ClientCoreError::from)?
|
||||
.with_retries(DEFAULT_NYM_API_RETRIES);
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
}
|
||||
|
||||
return builder.build().map_err(ClientCoreError::from);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to basic client for backwards compatibility
|
||||
tracing::debug!("Building basic nym-api HTTP client from config endpoints");
|
||||
|
||||
let mut nym_api_urls = config.get_nym_api_endpoints();
|
||||
if nym_api_urls.is_empty() {
|
||||
tracing::warn!("No API endpoints configured in config, this may cause issues");
|
||||
}
|
||||
nym_api_urls.shuffle(&mut thread_rng());
|
||||
|
||||
let mut builder = nym_http_api_client::Client::builder(nym_api_urls[0].clone())
|
||||
.map_err(ClientCoreError::from)?;
|
||||
// Convert config URLs to ApiUrl format for consistency
|
||||
let api_urls: Vec<nym_network_defaults::ApiUrl> = nym_api_urls
|
||||
.into_iter()
|
||||
.map(|url| nym_network_defaults::ApiUrl {
|
||||
url: url.to_string(),
|
||||
front_hosts: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
tracing::debug!("Using {} config API endpoints", api_urls.len());
|
||||
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_fronted_urls(api_urls)
|
||||
.map_err(ClientCoreError::from)?
|
||||
.with_retries(DEFAULT_NYM_API_RETRIES)
|
||||
.with_bincode();
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
}
|
||||
|
||||
builder = builder.with_bincode();
|
||||
|
||||
builder.build().map_err(ClientCoreError::from)
|
||||
}
|
||||
|
||||
@@ -940,8 +1004,8 @@ where
|
||||
// Create a shutdown tracker for this client - either as a child of provided tracker
|
||||
// or get one from the registry
|
||||
let shutdown_tracker = match self.shutdown {
|
||||
Some(parent_tracker) => parent_tracker.child_tracker(),
|
||||
None => nym_task::get_sdk_shutdown_tracker()?,
|
||||
Some(parent_tracker) => parent_tracker.clone(),
|
||||
None => nym_task::create_sdk_shutdown_tracker()?,
|
||||
};
|
||||
|
||||
Self::start_event_control(self.event_tx, event_receiver, &shutdown_tracker);
|
||||
@@ -961,7 +1025,11 @@ where
|
||||
.dkg_query_client
|
||||
.map(|client| BandwidthController::new(credential_store, client));
|
||||
|
||||
let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone())?;
|
||||
let nym_api_client = Self::construct_nym_api_client(
|
||||
self.nym_api_urls.as_ref(),
|
||||
&self.config,
|
||||
self.user_agent.clone(),
|
||||
)?;
|
||||
let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?;
|
||||
|
||||
let topology_provider = Self::setup_topology_provider(
|
||||
@@ -976,7 +1044,7 @@ where
|
||||
self.user_agent.clone(),
|
||||
generate_client_stats_id(*self_address.identity()),
|
||||
input_sender.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
|
||||
// needs to be started as the first thing to block if required waiting for the gateway
|
||||
@@ -986,7 +1054,7 @@ where
|
||||
shared_topology_accessor.clone(),
|
||||
self_address.gateway(),
|
||||
self.wait_for_gateway,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1006,7 +1074,7 @@ where
|
||||
stats_reporter.clone(),
|
||||
#[cfg(unix)]
|
||||
self.connection_fd_callback,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
)
|
||||
.await?;
|
||||
let gateway_ws_fd = gateway_transceiver.ws_fd();
|
||||
@@ -1014,7 +1082,7 @@ where
|
||||
let reply_storage = Self::setup_persistent_reply_storage(
|
||||
reply_storage_backend,
|
||||
key_rotation_config,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1025,7 +1093,7 @@ where
|
||||
reply_storage.key_storage(),
|
||||
reply_controller_sender.clone(),
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
|
||||
// The message_sender is the transmitter for any component generating sphinx packets
|
||||
@@ -1035,7 +1103,7 @@ where
|
||||
|
||||
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
|
||||
gateway_transceiver,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
EventSender(event_sender),
|
||||
);
|
||||
|
||||
@@ -1066,7 +1134,7 @@ where
|
||||
shared_lane_queue_lengths.clone(),
|
||||
client_connection_rx,
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
|
||||
if !self
|
||||
@@ -1082,7 +1150,7 @@ where
|
||||
shared_topology_accessor.clone(),
|
||||
message_sender,
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1136,3 +1204,53 @@ pub struct BaseClient {
|
||||
pub forget_me: ForgetMe,
|
||||
pub remember_me: RememberMe,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_network_defaults::{ApiUrl, NymNetworkDetails};
|
||||
|
||||
#[test]
|
||||
fn test_network_details_with_multiple_urls() {
|
||||
// Verify that network details can be configured with multiple API URLs
|
||||
let mut network_details = NymNetworkDetails::new_empty();
|
||||
network_details.nym_api_urls = Some(vec![
|
||||
ApiUrl {
|
||||
url: "https://validator.nymtech.net/api/".to_string(),
|
||||
front_hosts: None,
|
||||
},
|
||||
ApiUrl {
|
||||
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
|
||||
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(network_details.nym_api_urls.as_ref().unwrap().len(), 2);
|
||||
assert!(network_details.nym_api_urls.as_ref().unwrap()[1]
|
||||
.front_hosts
|
||||
.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_network_details_with_front_hosts() {
|
||||
// Verify that ApiUrl can store domain fronting configuration
|
||||
let api_url = ApiUrl {
|
||||
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
|
||||
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
|
||||
};
|
||||
|
||||
assert_eq!(api_url.url, "https://nym-frontdoor.vercel.app/api/");
|
||||
assert_eq!(api_url.front_hosts.as_ref().unwrap().len(), 2);
|
||||
assert!(api_url
|
||||
.front_hosts
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains(&"vercel.app".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_nym_api_retries_constant() {
|
||||
// Verify the retry constant is set correctly
|
||||
assert_eq!(DEFAULT_NYM_API_RETRIES, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
TrySendError::Full(_) => {
|
||||
// This isn't a problem, if the channel is full means we're already sending the
|
||||
// max amount of messages downstream can handle.
|
||||
tracing::debug!("Failed to send cover message - channel full");
|
||||
tracing::trace!("Failed to send cover message - channel full");
|
||||
}
|
||||
TrySendError::Closed(_) => {
|
||||
tracing::warn!("Failed to send cover message - channel closed");
|
||||
|
||||
@@ -20,7 +20,10 @@ pub mod transceiver;
|
||||
|
||||
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
|
||||
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
|
||||
const MAX_FAILURE_COUNT: usize = 100;
|
||||
|
||||
/// Reduced from 100 to 20 to fail fast (~1-2 seconds instead of ~6 seconds).
|
||||
/// If we can't send 20 packets in a row, the gateway is unreachable.
|
||||
const MAX_FAILURE_COUNT: usize = 20;
|
||||
|
||||
// that's also disgusting.
|
||||
pub struct Empty;
|
||||
@@ -84,7 +87,7 @@ impl MixTrafficController {
|
||||
self.client_tx.clone()
|
||||
}
|
||||
|
||||
pub fn mix_rx(&self) -> BatchMixMessageSender {
|
||||
pub fn mix_tx(&self) -> BatchMixMessageSender {
|
||||
self.mix_tx.clone()
|
||||
}
|
||||
|
||||
@@ -156,6 +159,11 @@ impl MixTrafficController {
|
||||
// Do we need to handle the embedded mixnet client case
|
||||
// separately?
|
||||
self.event_tx.send(MixnetClientEvent::Traffic(MixTrafficEvent::FailedSendingSphinx));
|
||||
// IMO it shouldn't be signalled from there but it is how it is
|
||||
// TODO : report the failure upwards and shutdown from upwards
|
||||
// Gateway is dead, we have to shut down currently
|
||||
error!("Signalling shutdown from the MixTrafficController");
|
||||
self.shutdown_token.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +298,8 @@ where
|
||||
"failed to send mixnet packet due to closed channel (outside of shutdown!)"
|
||||
);
|
||||
}
|
||||
// Early return to avoid further processing when channel is closed
|
||||
return;
|
||||
}
|
||||
Ok(_) => {
|
||||
let event = if fragment_id.is_some() {
|
||||
|
||||
@@ -45,7 +45,7 @@ pub enum ClientCoreError {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
ResolutionFailed(#[from] nym_http_api_client::ResolveError),
|
||||
|
||||
#[error("no gateways on network")]
|
||||
NoGatewaysOnNetwork,
|
||||
|
||||
@@ -151,7 +151,7 @@ pub async fn gateways_for_init(
|
||||
}
|
||||
|
||||
let retry_count = retry_count.unwrap_or(DEFAULT_NYM_API_RETRIES);
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())?
|
||||
.with_retries(retry_count)
|
||||
.with_bincode();
|
||||
|
||||
@@ -441,7 +441,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_multiple_urls_prepared_for_retries() {
|
||||
let urls = vec![
|
||||
let urls = [
|
||||
Url::parse("https://api1.nym.com").unwrap(),
|
||||
Url::parse("https://api2.nym.com").unwrap(),
|
||||
Url::parse("https://api3.nym.com").unwrap(),
|
||||
|
||||
@@ -30,7 +30,6 @@ pub(crate) async fn connect_async(
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -88,3 +88,6 @@ features = ["js"]
|
||||
|
||||
[features]
|
||||
wasm = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -26,6 +27,39 @@ pub struct ClientBandwidth {
|
||||
inner: Arc<ClientBandwidthInner>,
|
||||
}
|
||||
|
||||
// simple helper for logging purposes to accommodate 'unknown' case
|
||||
pub(crate) enum UpgradeModeEnabledWrapper {
|
||||
True,
|
||||
False,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<Option<bool>> for UpgradeModeEnabledWrapper {
|
||||
fn from(value: Option<bool>) -> Self {
|
||||
match value {
|
||||
Some(true) => UpgradeModeEnabledWrapper::True,
|
||||
Some(false) => UpgradeModeEnabledWrapper::False,
|
||||
None => UpgradeModeEnabledWrapper::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for UpgradeModeEnabledWrapper {
|
||||
fn from(value: bool) -> Self {
|
||||
Some(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UpgradeModeEnabledWrapper {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UpgradeModeEnabledWrapper::True => write!(f, "true"),
|
||||
UpgradeModeEnabledWrapper::False => write!(f, "false"),
|
||||
UpgradeModeEnabledWrapper::Unknown => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientBandwidthInner {
|
||||
/// the actual bandwidth amount (in bytes) available
|
||||
available: AtomicI64,
|
||||
@@ -71,26 +105,41 @@ impl ClientBandwidth {
|
||||
self.inner.available.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_log_bandwidth(&self, now: Option<OffsetDateTime>) {
|
||||
pub(crate) fn maybe_log_bandwidth(
|
||||
&self,
|
||||
now: Option<OffsetDateTime>,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let last = self.last_logged();
|
||||
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
|
||||
if last + Duration::from_secs(10) < now {
|
||||
self.log_bandwidth(Some(now))
|
||||
self.log_bandwidth(Some(now), upgrade_mode)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn log_bandwidth(&self, now: Option<OffsetDateTime>) {
|
||||
pub(crate) fn log_bandwidth(
|
||||
&self,
|
||||
now: Option<OffsetDateTime>,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
|
||||
let upgrade_mode = upgrade_mode.into();
|
||||
|
||||
let remaining = self.remaining();
|
||||
let remaining_bi2 = bibytes2(remaining as f64);
|
||||
|
||||
if remaining < 0 {
|
||||
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
|
||||
tracing::warn!(
|
||||
"OUT OF BANDWIDTH. remaining: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
|
||||
);
|
||||
} else if remaining < 1_000_000 {
|
||||
tracing::info!("remaining bandwidth: {remaining_bi2}");
|
||||
tracing::info!(
|
||||
"remaining bandwidth: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
|
||||
);
|
||||
} else {
|
||||
tracing::debug!("remaining bandwidth: {remaining_bi2}");
|
||||
tracing::trace!(
|
||||
"remaining bandwidth: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
|
||||
);
|
||||
}
|
||||
|
||||
self.inner
|
||||
@@ -98,26 +147,35 @@ impl ClientBandwidth {
|
||||
.store(now.unix_timestamp(), Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub(crate) fn update_and_maybe_log(&self, remaining: i64) {
|
||||
pub(crate) fn update_and_maybe_log(
|
||||
&self,
|
||||
remaining: i64,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
self.inner.available.store(remaining, Ordering::Release);
|
||||
self.inner
|
||||
.last_updated_ts
|
||||
.store(now.unix_timestamp(), Ordering::Relaxed);
|
||||
self.maybe_log_bandwidth(Some(now))
|
||||
self.maybe_log_bandwidth(Some(now), upgrade_mode)
|
||||
}
|
||||
|
||||
pub(crate) fn update_and_log(&self, remaining: i64) {
|
||||
pub(crate) fn update_and_log(
|
||||
&self,
|
||||
remaining: i64,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
self.inner.available.store(remaining, Ordering::Release);
|
||||
self.inner
|
||||
.last_updated_ts
|
||||
.store(now.unix_timestamp(), Ordering::Relaxed);
|
||||
self.log_bandwidth(Some(now))
|
||||
self.log_bandwidth(Some(now), upgrade_mode)
|
||||
}
|
||||
|
||||
fn last_logged(&self) -> OffsetDateTime {
|
||||
// SAFETY: this value is always populated with valid timestamps
|
||||
#[allow(clippy::unwrap_used)]
|
||||
OffsetDateTime::from_unix_timestamp(self.inner.last_logged_ts.load(Ordering::Relaxed))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use nym_network_defaults::TicketTypeRepr::V1MixnetEntry;
|
||||
use nym_credentials_interface::DEFAULT_MIXNET_REQUEST_BANDWIDTH_THRESHOLD;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -103,7 +103,7 @@ impl BandwidthTickets {
|
||||
|
||||
// 20% of entry ticket value
|
||||
pub const DEFAULT_REMAINING_BANDWIDTH_THRESHOLD: i64 =
|
||||
(V1MixnetEntry.bandwidth_value() / 5) as i64;
|
||||
DEFAULT_MIXNET_REQUEST_BANDWIDTH_THRESHOLD;
|
||||
|
||||
pub const DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD: Option<i64> = None;
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_gateway_requests::registration::handshake::client_handshake;
|
||||
use nym_gateway_requests::{
|
||||
BinaryRequest, ClientControlRequest, ClientRequest, GatewayProtocolVersionExt,
|
||||
GatewayRequestsError, SensitiveServerResponse, ServerResponse, SharedGatewayKey,
|
||||
SharedSymmetricKey, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION,
|
||||
BandwidthResponse, BinaryRequest, ClientControlRequest, ClientRequest, GatewayProtocolVersion,
|
||||
GatewayProtocolVersionExt, GatewayRequestsError, SensitiveServerResponse, ServerResponse,
|
||||
SharedGatewayKey, SharedSymmetricKey, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
|
||||
};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_statistics_common::clients::connection::ConnectionStatsEvent;
|
||||
@@ -101,8 +101,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
stats_reporter: ClientStatsSender,
|
||||
|
||||
// currently unused (but populated)
|
||||
negotiated_protocol: Option<u8>,
|
||||
negotiated_protocol: Option<GatewayProtocolVersion>,
|
||||
|
||||
// Callback on the fd as soon as the connection has been established
|
||||
#[cfg(unix)]
|
||||
@@ -166,10 +165,12 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[allow(clippy::unreachable)]
|
||||
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
|
||||
match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
|
||||
SocketState::Available(mut socket) => Ok((*socket).close(None).await?),
|
||||
SocketState::PartiallyDelegated(_) => {
|
||||
// SAFETY: this is only called after the caller has already recovered the connection
|
||||
unreachable!("this branch should have never been reached!")
|
||||
}
|
||||
_ => Ok(()), // no need to do anything in those cases
|
||||
@@ -177,6 +178,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[allow(clippy::unreachable)]
|
||||
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
|
||||
match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
|
||||
SocketState::Available(socket) => {
|
||||
@@ -184,6 +186,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
SocketState::PartiallyDelegated(_) => {
|
||||
// SAFETY: this is only called after the caller has already recovered the connection
|
||||
unreachable!("this branch should have never been reached!")
|
||||
}
|
||||
_ => Ok(()), // no need to do anything in those cases
|
||||
@@ -458,43 +461,16 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_gateway_protocol(
|
||||
&self,
|
||||
gateway_protocol: Option<u8>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
debug!("gateway protocol: {gateway_protocol:?}, ours: {CURRENT_PROTOCOL_VERSION}");
|
||||
|
||||
// right now there are no failure cases here, but this might change in the future
|
||||
match gateway_protocol {
|
||||
None => {
|
||||
warn!("the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0");
|
||||
// note: in +1.2.0 we will have to return a hard error here
|
||||
Ok(())
|
||||
}
|
||||
Some(v) if v > CURRENT_PROTOCOL_VERSION => {
|
||||
let err = GatewayClientError::IncompatibleProtocol {
|
||||
gateway: Some(v),
|
||||
current: CURRENT_PROTOCOL_VERSION,
|
||||
};
|
||||
error!("{err}");
|
||||
Err(err)
|
||||
}
|
||||
|
||||
Some(_) => {
|
||||
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn register(
|
||||
&mut self,
|
||||
derive_aes256_gcm_siv_key: bool,
|
||||
supported_gateway_protocol: Option<GatewayProtocolVersion>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
}
|
||||
|
||||
let derive_aes256_gcm_siv_key = supported_gateway_protocol.supports_aes256_gcm_siv();
|
||||
|
||||
debug_assert!(self.connection.is_available());
|
||||
log::debug!(
|
||||
"registering with gateway. using legacy key derivation: {}",
|
||||
@@ -505,14 +481,13 @@ impl<C, St> GatewayClient<C, St> {
|
||||
// and putting it into the GatewayClient struct would be a hassle
|
||||
let mut rng = OsRng;
|
||||
|
||||
let shared_key = match &mut self.connection {
|
||||
let handshake_result = match &mut self.connection {
|
||||
SocketState::Available(ws_stream) => client_handshake(
|
||||
&mut rng,
|
||||
ws_stream,
|
||||
self.local_identity.as_ref(),
|
||||
self.gateway_identity,
|
||||
self.cfg.bandwidth.require_tickets,
|
||||
derive_aes256_gcm_siv_key,
|
||||
supported_gateway_protocol,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
self.shutdown_token.clone(),
|
||||
)
|
||||
@@ -521,26 +496,31 @@ impl<C, St> GatewayClient<C, St> {
|
||||
_ => return Err(GatewayClientError::ConnectionInInvalidState),
|
||||
}?;
|
||||
|
||||
let (authentication_status, gateway_protocol) = match self.read_control_response().await? {
|
||||
let authentication_status = match self.read_control_response().await? {
|
||||
ServerResponse::Register {
|
||||
protocol_version,
|
||||
status,
|
||||
} => (status, protocol_version),
|
||||
upgrade_mode,
|
||||
..
|
||||
} => {
|
||||
if upgrade_mode {
|
||||
warn!("the system is currently undergoing an upgrade. some of its functionalities might be unstable")
|
||||
}
|
||||
status
|
||||
}
|
||||
ServerResponse::Error { message } => {
|
||||
return Err(GatewayClientError::GatewayError(message))
|
||||
}
|
||||
other => return Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
};
|
||||
|
||||
self.check_gateway_protocol(gateway_protocol)?;
|
||||
self.authenticated = authentication_status;
|
||||
|
||||
if self.authenticated {
|
||||
self.shared_key = Some(Arc::new(shared_key));
|
||||
self.shared_key = Some(Arc::new(handshake_result.derived_key));
|
||||
}
|
||||
|
||||
// populate the negotiated protocol for future uses
|
||||
self.negotiated_protocol = gateway_protocol;
|
||||
self.negotiated_protocol = Some(handshake_result.negotiated_protocol);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -623,13 +603,24 @@ impl<C, St> GatewayClient<C, St> {
|
||||
protocol_version,
|
||||
status,
|
||||
bandwidth_remaining,
|
||||
upgrade_mode,
|
||||
} => {
|
||||
self.check_gateway_protocol(protocol_version)?;
|
||||
if protocol_version.is_future_version() {
|
||||
// SAFETY: future version is always defined
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let version = protocol_version.unwrap();
|
||||
error!("the gateway insists on using v{version} protocol which is not supported by this client");
|
||||
return Err(GatewayClientError::AuthenticationFailure);
|
||||
}
|
||||
self.authenticated = status;
|
||||
self.bandwidth.update_and_maybe_log(bandwidth_remaining);
|
||||
self.bandwidth
|
||||
.update_and_maybe_log(bandwidth_remaining, upgrade_mode);
|
||||
|
||||
self.negotiated_protocol = protocol_version;
|
||||
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
|
||||
if upgrade_mode {
|
||||
warn!("the system is currently undergoing an upgrade. some of its functionalities might be unstable")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -650,7 +641,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
.public_key()
|
||||
.derive_destination_address();
|
||||
|
||||
let msg = ClientControlRequest::new_authenticate(
|
||||
let msg = ClientControlRequest::new_legacy_authenticate(
|
||||
self_address,
|
||||
shared_key,
|
||||
self.cfg.bandwidth.require_tickets,
|
||||
@@ -659,25 +650,40 @@ impl<C, St> GatewayClient<C, St> {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn authenticate_v2(&mut self) -> Result<(), GatewayClientError> {
|
||||
async fn authenticate_v2(
|
||||
&mut self,
|
||||
requested_protocol_version: GatewayProtocolVersion,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
debug!("using v2 authentication");
|
||||
let Some(shared_key) = self.shared_key.as_ref() else {
|
||||
return Err(GatewayClientError::NoSharedKeyAvailable);
|
||||
};
|
||||
|
||||
let msg = ClientControlRequest::new_authenticate_v2(shared_key, &self.local_identity)?;
|
||||
let msg = ClientControlRequest::new_authenticate_v2(
|
||||
shared_key,
|
||||
&self.local_identity,
|
||||
requested_protocol_version,
|
||||
)?;
|
||||
self.send_authenticate_request_and_handle_response(msg)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn authenticate(&mut self, use_v2: bool) -> Result<(), GatewayClientError> {
|
||||
async fn authenticate(
|
||||
&mut self,
|
||||
supported_gateway_protocol: Option<GatewayProtocolVersion>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
}
|
||||
debug!("authenticating with gateway");
|
||||
|
||||
if use_v2 {
|
||||
self.authenticate_v2().await
|
||||
if supported_gateway_protocol.supports_authenticate_v2() {
|
||||
// use the highest possible protocol version the gateway has announced support for
|
||||
|
||||
// SAFETY: if announced protocol supports auth v2, it means it's properly set
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.authenticate_v2(supported_gateway_protocol.unwrap())
|
||||
.await
|
||||
} else {
|
||||
self.authenticate_v1().await
|
||||
}
|
||||
@@ -708,9 +714,12 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
};
|
||||
|
||||
debug!("supported gateway protocol: {gw_protocol:?}");
|
||||
|
||||
let supports_aes_gcm_siv = gw_protocol.supports_aes256_gcm_siv();
|
||||
let supports_auth_v2 = gw_protocol.supports_authenticate_v2();
|
||||
let supports_key_rotation_info = gw_protocol.supports_key_rotation_packet();
|
||||
let supports_upgrade_mode = gw_protocol.supports_upgrade_mode();
|
||||
|
||||
if !supports_aes_gcm_siv {
|
||||
warn!("this gateway is on an old version that doesn't support AES256-GCM-SIV");
|
||||
@@ -721,6 +730,16 @@ impl<C, St> GatewayClient<C, St> {
|
||||
if !supports_key_rotation_info {
|
||||
warn!("this gateway is on an old version that doesn't support key rotation packets")
|
||||
}
|
||||
if !supports_upgrade_mode {
|
||||
warn!("this gateway is on an old version that doesn't support upgrade mode")
|
||||
}
|
||||
|
||||
let gw_protocol = if gw_protocol.is_future_version() {
|
||||
warn!("we're running outdated software as gateway is announcing protocol {gw_protocol:?} whilst we're using {}. we're going to attempt to downgrade", GatewayProtocolVersion::CURRENT);
|
||||
Some(GatewayProtocolVersion::CURRENT)
|
||||
} else {
|
||||
gw_protocol
|
||||
};
|
||||
|
||||
if self.authenticated {
|
||||
debug!("Already authenticated");
|
||||
@@ -735,10 +754,11 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
if self.shared_key.is_some() {
|
||||
self.authenticate(supports_auth_v2).await?;
|
||||
self.authenticate(gw_protocol).await?;
|
||||
|
||||
if self.authenticated {
|
||||
// if we are authenticated it means we MUST have an associated shared_key
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let shared_key = self.shared_key.as_ref().unwrap();
|
||||
|
||||
let requires_key_upgrade = shared_key.is_legacy() && supports_aes_gcm_siv;
|
||||
@@ -751,9 +771,10 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Err(GatewayClientError::AuthenticationFailure)
|
||||
}
|
||||
} else {
|
||||
self.register(supports_aes_gcm_siv).await?;
|
||||
self.register(gw_protocol).await?;
|
||||
|
||||
// if registration didn't return an error, we MUST have an associated shared key
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let shared_key = self.shared_key.as_ref().unwrap();
|
||||
|
||||
// we're always registering with the highest supported protocol,
|
||||
@@ -783,51 +804,81 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn claim_ecash_bandwidth(
|
||||
async fn wait_for_bandwidth_response(
|
||||
&mut self,
|
||||
credential: CredentialSpendingData,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::new_enc_ecash_credential(
|
||||
credential,
|
||||
self.shared_key.as_ref().unwrap(),
|
||||
)?;
|
||||
let bandwidth_remaining = match self
|
||||
msg: ClientControlRequest,
|
||||
) -> Result<BandwidthResponse, GatewayClientError> {
|
||||
let response = match self
|
||||
.send_websocket_message_with_non_send_response(msg)
|
||||
.await?
|
||||
{
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
ServerResponse::Bandwidth(response) => {
|
||||
if response.upgrade_mode {
|
||||
info!("the system is currently undergoing an upgrade. our bandwidth shouldn't have been metered")
|
||||
}
|
||||
Ok(response)
|
||||
}
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
ServerResponse::TypedError { error } => {
|
||||
Err(GatewayClientError::TypedGatewayError(error))
|
||||
}
|
||||
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
}?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn claim_ecash_bandwidth(
|
||||
&mut self,
|
||||
credential: CredentialSpendingData,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
// SAFETY: claiming ecash bandwidth is called as part of `claim_bandwidth` which
|
||||
// ensures the shared key is defined
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let msg = ClientControlRequest::new_enc_ecash_credential(
|
||||
credential,
|
||||
self.shared_key.as_ref().unwrap(),
|
||||
)?;
|
||||
let response = self.wait_for_bandwidth_response(msg).await?;
|
||||
|
||||
// TODO: create tracing span
|
||||
info!("managed to claim ecash bandwidth");
|
||||
self.bandwidth.update_and_log(bandwidth_remaining);
|
||||
self.bandwidth
|
||||
.update_and_log(response.available_total, response.upgrade_mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_upgrade_mode_jwt(&mut self, token: String) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::new_upgrade_mode_jwt(token);
|
||||
let response = self.wait_for_bandwidth_response(msg).await?;
|
||||
|
||||
// if gateway rejected our jwt, we would have returned an error
|
||||
info!("gateway has accepted our jwt");
|
||||
if !response.upgrade_mode {
|
||||
error!("but we're not in upgrade mode - something is wrong!");
|
||||
return Err(GatewayClientError::UnexpectedUpgradeModeState);
|
||||
}
|
||||
|
||||
self.bandwidth
|
||||
.update_and_log(response.available_total, response.upgrade_mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth;
|
||||
let bandwidth_remaining = match self
|
||||
.send_websocket_message_with_non_send_response(msg)
|
||||
.await?
|
||||
{
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
}?;
|
||||
let response = self.wait_for_bandwidth_response(msg).await?;
|
||||
|
||||
info!("managed to claim testnet bandwidth");
|
||||
self.bandwidth.update_and_log(bandwidth_remaining);
|
||||
self.bandwidth
|
||||
.update_and_log(response.available_total, response.upgrade_mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unchecked_bandwidth_controller(&self) -> &BandwidthController<C, St> {
|
||||
// this is an unchecked method
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.bandwidth_controller.as_ref().unwrap()
|
||||
}
|
||||
|
||||
@@ -919,6 +970,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
BinaryRequest::ForwardSphinx { packet }
|
||||
};
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
req.into_ws_message(
|
||||
self.shared_key
|
||||
.as_ref()
|
||||
@@ -1025,6 +1077,8 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.send_with_reconnection_on_failure(msg).await
|
||||
}
|
||||
|
||||
// SAFETY: this method is only called when the connection is in `PartiallyDelegated` state
|
||||
#[allow(clippy::unreachable)]
|
||||
async fn recover_socket_connection(&mut self) -> Result<(), GatewayClientError> {
|
||||
if self.connection.is_available() {
|
||||
return Ok(());
|
||||
@@ -1054,6 +1108,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
return Err(GatewayClientError::ConnectionInInvalidState);
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let partially_delegated =
|
||||
match std::mem::replace(&mut self.connection, SocketState::Invalid) {
|
||||
SocketState::Available(conn) => {
|
||||
@@ -1069,7 +1124,13 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.shutdown_token.clone(),
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
other => {
|
||||
error!(
|
||||
"attempted to start mixnet listener whilst the connection is in {} state!",
|
||||
other.name()
|
||||
);
|
||||
return Err(GatewayClientError::ConnectionInInvalidState);
|
||||
}
|
||||
};
|
||||
|
||||
self.connection = SocketState::PartiallyDelegated(partially_delegated);
|
||||
@@ -1082,8 +1143,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
// if we're reconnecting, because we lost connection, we need to re-authenticate the connection
|
||||
self.authenticate(self.negotiated_protocol.supports_authenticate_v2())
|
||||
.await?;
|
||||
self.authenticate(self.negotiated_protocol).await?;
|
||||
|
||||
// this call is NON-blocking
|
||||
self.start_listening_for_mixnet_messages()?;
|
||||
|
||||
@@ -39,7 +39,6 @@ pub(crate) async fn connect_async(
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ pub enum GatewayClientError {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
ResolutionFailed(#[from] nym_http_api_client::ResolveError),
|
||||
|
||||
#[error("No shared key was provided or obtained")]
|
||||
NoSharedKeyAvailable,
|
||||
@@ -128,6 +128,9 @@ pub enum GatewayClientError {
|
||||
"this operation couldn't be completed as the program is in the process of shutting down"
|
||||
)]
|
||||
ShutdownInProgress,
|
||||
|
||||
#[error("the system is an unexpected upgrade mode state")]
|
||||
UnexpectedUpgradeModeState,
|
||||
}
|
||||
|
||||
impl From<WsError> for GatewayClientError {
|
||||
|
||||
@@ -35,6 +35,7 @@ impl PacketRouter {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
pub fn route_mixnet_messages(
|
||||
&self,
|
||||
received_messages: Vec<Vec<u8>>,
|
||||
@@ -54,6 +55,7 @@ impl PacketRouter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
pub fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), GatewayClientError> {
|
||||
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
|
||||
// check if the failure is due to the shutdown being in progress and thus the receiver channel
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::bandwidth::ClientBandwidth;
|
||||
use crate::client::config::BandwidthTickets;
|
||||
use crate::error::GatewayClientError;
|
||||
use crate::packet_router::PacketRouter;
|
||||
use crate::traits::GatewayPacketRouter;
|
||||
@@ -10,7 +11,9 @@ use futures::channel::oneshot;
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use nym_gateway_requests::shared_key::SharedGatewayKey;
|
||||
use nym_gateway_requests::{SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError};
|
||||
use nym_gateway_requests::{
|
||||
SendResponse, SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError,
|
||||
};
|
||||
use nym_task::ShutdownToken;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::os::raw::c_int as RawFd;
|
||||
@@ -154,11 +157,12 @@ impl PartiallyDelegatedRouter {
|
||||
fn handle_text_message(&self, text: String) -> Result<(), GatewayClientError> {
|
||||
// if we fail to deserialise the response, return a hard error. we can't handle garbage
|
||||
match ServerResponse::try_from(text).map_err(|_| GatewayClientError::MalformedResponse)? {
|
||||
ServerResponse::Send {
|
||||
ServerResponse::Send(SendResponse {
|
||||
remaining_bandwidth,
|
||||
} => {
|
||||
upgrade_mode,
|
||||
}) => {
|
||||
self.client_bandwidth
|
||||
.update_and_maybe_log(remaining_bandwidth);
|
||||
.update_and_maybe_log(remaining_bandwidth, upgrade_mode);
|
||||
Ok(())
|
||||
}
|
||||
ServerResponse::Error { message } => {
|
||||
@@ -174,7 +178,20 @@ impl PartiallyDelegatedRouter {
|
||||
let available_bi2 = bibytes2(available as f64);
|
||||
let required_bi2 = bibytes2(required as f64);
|
||||
warn!("run out of bandwidth when attempting to send the message! we got {available_bi2} available, but needed at least {required_bi2} to send the previous message");
|
||||
self.client_bandwidth.update_and_log(available);
|
||||
// if we run out of bandwidth (and tried to send reasonable amount of data),
|
||||
// the upgrade mode is implicitly disabled, as otherwise we would have been
|
||||
// to proceed
|
||||
let upgrade_mode = if available
|
||||
< BandwidthTickets::DEFAULT_REMAINING_BANDWIDTH_THRESHOLD
|
||||
{
|
||||
Some(false)
|
||||
} else {
|
||||
// we were attempting to send a lot of data at once
|
||||
// - we have no certainty about upgrade mode at this point
|
||||
None
|
||||
};
|
||||
self.client_bandwidth
|
||||
.update_and_log(available, upgrade_mode);
|
||||
// UNIMPLEMENTED: we should stop sending messages until we recover bandwidth
|
||||
Ok(())
|
||||
}
|
||||
@@ -327,6 +344,7 @@ impl PartiallyDelegatedHandle {
|
||||
Ok(self.sink_half.send_all(&mut send_stream).await?)
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
pub(crate) async fn merge(self) -> Result<WsConn, GatewayClientError> {
|
||||
let (mut stream_receiver, notify) = self.delegated_stream;
|
||||
|
||||
@@ -355,8 +373,10 @@ impl PartiallyDelegatedHandle {
|
||||
// in receive_res
|
||||
.map_err(|_| GatewayClientError::ConnectionAbruptlyClosed)?;
|
||||
let stream = stream_results?;
|
||||
|
||||
// the error is thrown when trying to reunite sink and stream that did not originate
|
||||
// from the same split which is impossible to happen here
|
||||
#[allow(clippy::unwrap_used)]
|
||||
Ok(self.sink_half.reunite(stream).unwrap())
|
||||
}
|
||||
}
|
||||
@@ -387,4 +407,13 @@ impl SocketState {
|
||||
SocketState::Available(_) | SocketState::PartiallyDelegated(_)
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> &'static str {
|
||||
match self {
|
||||
SocketState::Available(_) => "available",
|
||||
SocketState::PartiallyDelegated(_) => "partially delegated",
|
||||
SocketState::NotConnected => "not connected",
|
||||
SocketState::Invalid => "invalid",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ workspace = true
|
||||
features = ["json", "rustls-tls"]
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
cosmrs = { workspace = true, features = ["bip32"] }
|
||||
ts-rs = { workspace = true }
|
||||
|
||||
@@ -7,6 +7,7 @@ use cosmrs::{tx, AccountId, Coin, Denom};
|
||||
use nym_validator_client::http_client;
|
||||
use nym_validator_client::nyxd::CosmWasmClient;
|
||||
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
use nym_validator_client::signing::signer::OfflineSigner;
|
||||
use nym_validator_client::signing::tx_signer::TxSigner;
|
||||
use nym_validator_client::signing::SignerData;
|
||||
|
||||
@@ -19,8 +20,8 @@ async fn main() {
|
||||
let validator = "https://rpc.sandbox.nymtech.net";
|
||||
let to_address: AccountId = "n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa".parse().unwrap();
|
||||
|
||||
let signer = DirectSecp256k1HdWallet::from_mnemonic(prefix, signer_mnemonic);
|
||||
let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone();
|
||||
let signer = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, signer_mnemonic).unwrap();
|
||||
let signer_address = signer.signer_addresses()[0].clone();
|
||||
|
||||
// local 'client' ONLY signing messages
|
||||
let tx_signer = signer;
|
||||
@@ -57,9 +58,15 @@ async fn main() {
|
||||
100000u32,
|
||||
);
|
||||
|
||||
let tx_raw = tx_signer
|
||||
.sign_direct(&signer_address, vec![send_msg], fee, memo, signer_data)
|
||||
.unwrap();
|
||||
let tx_raw = TxSigner::sign_direct(
|
||||
&tx_signer,
|
||||
&signer_address,
|
||||
vec![send_msg],
|
||||
fee,
|
||||
memo,
|
||||
signer_data,
|
||||
)
|
||||
.unwrap();
|
||||
let tx_bytes = tx_raw.to_bytes().unwrap();
|
||||
|
||||
// compare balances from before and after the tx
|
||||
|
||||
@@ -5,8 +5,7 @@ use crate::nyxd::{self, NyxdClient};
|
||||
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
use crate::signing::signer::{NoSigner, OfflineSigner};
|
||||
use crate::{
|
||||
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ReqwestRpcClient,
|
||||
ValidatorClientError,
|
||||
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ValidatorClientError,
|
||||
};
|
||||
use nym_api_requests::ecash::models::{
|
||||
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
||||
@@ -164,7 +163,7 @@ impl Client<HttpRpcClient, DirectSecp256k1HdWallet> {
|
||||
) -> Result<DirectSigningHttpRpcValidatorClient, ValidatorClientError> {
|
||||
let rpc_client = http_client(config.nyxd_url.as_str())?;
|
||||
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||
let wallet = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic)?;
|
||||
|
||||
Ok(Self::new_signing_with_rpc_client(
|
||||
config, rpc_client, wallet,
|
||||
@@ -177,12 +176,13 @@ impl Client<HttpRpcClient, DirectSecp256k1HdWallet> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Client<ReqwestRpcClient, DirectSecp256k1HdWallet> {
|
||||
#[allow(deprecated)]
|
||||
impl Client<crate::ReqwestRpcClient, DirectSecp256k1HdWallet> {
|
||||
pub fn new_reqwest_signing(
|
||||
config: Config,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
) -> DirectSigningReqwestRpcValidatorClient {
|
||||
let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone());
|
||||
let rpc_client = crate::ReqwestRpcClient::new(config.nyxd_url.clone());
|
||||
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||
|
||||
@@ -203,9 +203,10 @@ impl Client<HttpRpcClient> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Client<ReqwestRpcClient> {
|
||||
#[allow(deprecated)]
|
||||
impl Client<crate::ReqwestRpcClient> {
|
||||
pub fn new_reqwest_query(config: Config) -> QueryReqwestRpcValidatorClient {
|
||||
let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone());
|
||||
let rpc_client = crate::ReqwestRpcClient::new(config.nyxd_url.clone());
|
||||
Self::new_with_rpc_client(config, rpc_client)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nym_api;
|
||||
use crate::signing::direct_wallet::DirectSecp256k1HdWalletError;
|
||||
pub use tendermint_rpc::error::Error as TendermintRpcError;
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -26,6 +27,12 @@ pub enum ValidatorClientError {
|
||||
|
||||
#[error("No validator API url has been provided")]
|
||||
NoAPIUrlAvailable,
|
||||
|
||||
#[error("failed to derive signing accounts: {source}")]
|
||||
AccountDerivationFailure {
|
||||
#[from]
|
||||
source: DirectSecp256k1HdWalletError,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<nym_api::error::NymAPIError> for ValidatorClientError {
|
||||
|
||||
@@ -12,6 +12,7 @@ pub mod rpc;
|
||||
pub mod signing;
|
||||
|
||||
pub use crate::error::ValidatorClientError;
|
||||
#[allow(deprecated)]
|
||||
pub use crate::rpc::reqwest::ReqwestRpcClient;
|
||||
pub use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
pub use client::{Client, Config, EcashApiClient};
|
||||
@@ -38,9 +39,13 @@ pub type DirectSigningHttpRpcValidatorClient = Client<HttpRpcClient, DirectSecp2
|
||||
#[cfg(feature = "http-client")]
|
||||
pub type DirectSigningHttpRpcNyxdClient = nyxd::NyxdClient<HttpRpcClient, DirectSecp256k1HdWallet>;
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub type QueryReqwestRpcValidatorClient = Client<ReqwestRpcClient>;
|
||||
#[allow(deprecated)]
|
||||
pub type QueryReqwestRpcNyxdClient = nyxd::NyxdClient<ReqwestRpcClient>;
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub type DirectSigningReqwestRpcValidatorClient = Client<ReqwestRpcClient, DirectSecp256k1HdWallet>;
|
||||
#[allow(deprecated)]
|
||||
pub type DirectSigningReqwestRpcNyxdClient =
|
||||
nyxd::NyxdClient<ReqwestRpcClient, DirectSecp256k1HdWallet>;
|
||||
|
||||
@@ -178,7 +178,7 @@ where
|
||||
.ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?;
|
||||
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
let signer_address = &self.signer_addresses()?[0];
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
|
||||
self.execute(signer_address, dkg_contract_address, &msg, fee, memo, funds)
|
||||
.await
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ where
|
||||
.ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?;
|
||||
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
let signer_address = &self.signer_addresses()?[0];
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
|
||||
self.execute(
|
||||
signer_address,
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ where
|
||||
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
|
||||
let signer_address = &self.signer_addresses()?[0];
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
self.execute(
|
||||
signer_address,
|
||||
group_contract_address,
|
||||
|
||||
+1
-1
@@ -667,7 +667,7 @@ where
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
let memo = msg.default_memo();
|
||||
|
||||
let signer_address = &self.signer_addresses()?[0];
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
self.execute(
|
||||
signer_address,
|
||||
mixnet_contract_address,
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ where
|
||||
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
|
||||
let signer_address = &self.signer_addresses()?[0];
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
self.execute(
|
||||
signer_address,
|
||||
multisig_contract_address,
|
||||
|
||||
+1
-1
@@ -165,7 +165,7 @@ where
|
||||
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
|
||||
let signer_address = &self.signer_addresses()?[0];
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
self.execute(
|
||||
signer_address,
|
||||
performance_contract_address,
|
||||
|
||||
+1
-1
@@ -375,7 +375,7 @@ where
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
let memo = msg.name().to_string();
|
||||
|
||||
let signer_address = &self.signer_addresses()?[0];
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
self.execute(
|
||||
signer_address,
|
||||
vesting_contract_address,
|
||||
|
||||
@@ -324,7 +324,7 @@ where
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
|
||||
fn get_accounts(&self) -> &[AccountData] {
|
||||
self.signer.get_accounts()
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::signing::signer::NoSigner;
|
||||
use crate::signing::signer::OfflineSigner;
|
||||
use crate::signing::tx_signer::TxSigner;
|
||||
use crate::signing::AccountData;
|
||||
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient};
|
||||
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
|
||||
use cosmrs::tx::{Raw, SignDoc};
|
||||
@@ -158,12 +158,13 @@ impl NyxdClient<HttpClient> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NyxdClient<ReqwestRpcClient> {
|
||||
#[allow(deprecated)]
|
||||
impl NyxdClient<crate::ReqwestRpcClient> {
|
||||
pub fn connect_reqwest(
|
||||
config: Config,
|
||||
endpoint: Url,
|
||||
) -> Result<QueryReqwestRpcNyxdClient, NyxdError> {
|
||||
let client = ReqwestRpcClient::new(endpoint);
|
||||
let client = crate::ReqwestRpcClient::new(endpoint);
|
||||
|
||||
Ok(NyxdClient {
|
||||
client: MaybeSigningClient::new(client, (&config).into()),
|
||||
@@ -195,18 +196,19 @@ impl NyxdClient<HttpClient, DirectSecp256k1HdWallet> {
|
||||
let client = http_client(endpoint)?;
|
||||
|
||||
let prefix = &config.chain_details.bech32_account_prefix;
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||
let wallet = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic)?;
|
||||
Ok(Self::connect_with_signer(config, client, wallet))
|
||||
}
|
||||
}
|
||||
|
||||
impl NyxdClient<ReqwestRpcClient, DirectSecp256k1HdWallet> {
|
||||
#[allow(deprecated)]
|
||||
impl NyxdClient<crate::ReqwestRpcClient, DirectSecp256k1HdWallet> {
|
||||
pub fn connect_reqwest_with_mnemonic(
|
||||
config: Config,
|
||||
endpoint: Url,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
) -> DirectSigningReqwestRpcNyxdClient {
|
||||
let client = ReqwestRpcClient::new(endpoint);
|
||||
let client = crate::ReqwestRpcClient::new(endpoint);
|
||||
|
||||
let prefix = &config.chain_details.bech32_account_prefix;
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||
@@ -391,17 +393,12 @@ where
|
||||
S: OfflineSigner + Send + Sync,
|
||||
NyxdError: From<<S as OfflineSigner>::Error>,
|
||||
{
|
||||
pub fn signing_account(&self) -> Result<AccountData, NyxdError> {
|
||||
pub fn signing_account(&self) -> Result<&AccountData, NyxdError> {
|
||||
Ok(self.find_account(&self.address())?)
|
||||
}
|
||||
|
||||
pub fn address(&self) -> AccountId {
|
||||
match self.client.signer_addresses() {
|
||||
Ok(addresses) => addresses[0].clone(),
|
||||
Err(_) => {
|
||||
panic!("key derivation failure")
|
||||
}
|
||||
}
|
||||
self.client.signer_addresses()[0].clone()
|
||||
}
|
||||
|
||||
pub fn mix_coin(&self, amount: u128) -> Coin {
|
||||
@@ -867,7 +864,7 @@ where
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
|
||||
fn get_accounts(&self) -> &[AccountData] {
|
||||
self.client.get_accounts()
|
||||
}
|
||||
|
||||
|
||||
@@ -42,12 +42,15 @@ macro_rules! perform_with_compat {
|
||||
}};
|
||||
}
|
||||
|
||||
// the separate implementation is now completely redundant
|
||||
#[deprecated(note = "use HttpClient directly instead")]
|
||||
pub struct ReqwestRpcClient {
|
||||
compat: CompatMode,
|
||||
inner: reqwest::Client,
|
||||
url: Url,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl ReqwestRpcClient {
|
||||
pub fn new(url: Url) -> Self {
|
||||
ReqwestRpcClient {
|
||||
@@ -131,6 +134,7 @@ impl TendermintRpcErrorMap for reqwest::Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl TendermintRpcClient for ReqwestRpcClient {
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::signing::signer::{OfflineSigner, SigningError};
|
||||
use crate::signing::{AccountData, Secp256k1Derivation};
|
||||
use cosmrs::bip32::{DerivationPath, XPrv};
|
||||
use cosmrs::crypto::secp256k1::SigningKey;
|
||||
use cosmrs::crypto::PublicKey;
|
||||
use crate::signing::{
|
||||
derive_extended_private_key, derive_keypair, AccountData, Secp256k1Derivation, Secp256k1Keypair,
|
||||
};
|
||||
use bip32::XPrv;
|
||||
use cosmrs::bip32::DerivationPath;
|
||||
use cosmrs::tx;
|
||||
use cosmrs::tx::SignDoc;
|
||||
use nym_config::defaults;
|
||||
use std::borrow::Cow;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
||||
|
||||
type Secp256k1Keypair = (SigningKey, PublicKey);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DirectSecp256k1HdWalletError {
|
||||
#[error(transparent)]
|
||||
@@ -36,28 +36,22 @@ pub enum DirectSecp256k1HdWalletError {
|
||||
}
|
||||
|
||||
// TODO: maybe lock this one behind feature flag?
|
||||
#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)]
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct DirectSecp256k1HdWallet {
|
||||
/// Base secret
|
||||
secret: bip39::Mnemonic,
|
||||
|
||||
/// BIP39 seed
|
||||
seed: [u8; 64],
|
||||
|
||||
// An unfortunate result of immature rust async story is that async traits (only available in the separate package)
|
||||
// can't yet figure out everything and if we stored our derived account data on the struct,
|
||||
// that would include the secret key which is a dyn EcdsaSigner and hence not Sync making the wallet
|
||||
// not Sync and if used on the signing client in an async trait, it wouldn't be Send
|
||||
/// Derivation instructions
|
||||
/// Derived accounts
|
||||
#[zeroize(skip)]
|
||||
accounts: Vec<Secp256k1Derivation>,
|
||||
// unfortunately `dyn EcdsaSigner` does not guarantee Zeroize
|
||||
accounts: Vec<AccountData>,
|
||||
}
|
||||
|
||||
impl OfflineSigner for DirectSecp256k1HdWallet {
|
||||
type Error = DirectSecp256k1HdWalletError;
|
||||
|
||||
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
|
||||
self.try_derive_accounts()
|
||||
fn get_accounts(&self) -> &[AccountData] {
|
||||
&self.accounts
|
||||
}
|
||||
|
||||
fn sign_direct_with_account(
|
||||
@@ -77,55 +71,27 @@ impl DirectSecp256k1HdWallet {
|
||||
}
|
||||
|
||||
/// Restores a wallet from the given BIP39 mnemonic using default options.
|
||||
#[deprecated(
|
||||
note = "this function can potentially panic if accounts can't be derived correctly. please use .checked_from_mnemonic() instead"
|
||||
)]
|
||||
pub fn from_mnemonic(prefix: &str, mnemonic: bip39::Mnemonic) -> Self {
|
||||
// unfortunately due to backwards compatibility requirements,
|
||||
// we can't change signature of this method
|
||||
#[allow(deprecated)]
|
||||
DirectSecp256k1HdWalletBuilder::new(prefix).build(mnemonic)
|
||||
}
|
||||
|
||||
/// Restores a wallet from the given BIP39 mnemonic using default options.
|
||||
pub fn checked_from_mnemonic(
|
||||
prefix: &str,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
) -> Result<Self, DirectSecp256k1HdWalletError> {
|
||||
DirectSecp256k1HdWalletBuilder::new(prefix).try_build(mnemonic)
|
||||
}
|
||||
|
||||
pub fn generate(prefix: &str, word_count: usize) -> Result<Self, DirectSecp256k1HdWalletError> {
|
||||
let mneomonic = bip39::Mnemonic::generate(word_count)?;
|
||||
Ok(Self::from_mnemonic(prefix, mneomonic))
|
||||
}
|
||||
|
||||
fn derive_keypair(
|
||||
&self,
|
||||
hd_path: &DerivationPath,
|
||||
) -> Result<Secp256k1Keypair, DirectSecp256k1HdWalletError> {
|
||||
let extended_private_key = XPrv::derive_from_path(self.seed, hd_path)?;
|
||||
|
||||
let private_key: SigningKey = extended_private_key.into();
|
||||
let public_key = private_key.public_key();
|
||||
|
||||
Ok((private_key, public_key))
|
||||
}
|
||||
|
||||
pub fn derive_extended_private_key(
|
||||
&self,
|
||||
hd_path: &DerivationPath,
|
||||
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
|
||||
Ok(XPrv::derive_from_path(self.seed, hd_path)?)
|
||||
}
|
||||
|
||||
pub fn try_derive_accounts(&self) -> Result<Vec<AccountData>, DirectSecp256k1HdWalletError> {
|
||||
let mut accounts = Vec::with_capacity(self.accounts.len());
|
||||
for derivation_info in &self.accounts {
|
||||
let keypair = self.derive_keypair(&derivation_info.hd_path)?;
|
||||
|
||||
// it seems this can only fail if the provided account prefix is invalid
|
||||
let address = keypair
|
||||
.1
|
||||
.account_id(&derivation_info.prefix)
|
||||
.map_err(
|
||||
|source| DirectSecp256k1HdWalletError::AccountDerivationError { source },
|
||||
)?;
|
||||
|
||||
accounts.push(AccountData {
|
||||
address,
|
||||
public_key: keypair.1,
|
||||
private_key: keypair.0,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(accounts)
|
||||
Self::checked_from_mnemonic(prefix, mneomonic)
|
||||
}
|
||||
|
||||
pub fn secret(&self) -> &bip39::Mnemonic {
|
||||
@@ -142,6 +108,43 @@ impl DirectSecp256k1HdWallet {
|
||||
pub fn mnemonic_string(&self) -> Zeroizing<String> {
|
||||
Zeroizing::new(self.secret.to_string())
|
||||
}
|
||||
|
||||
pub fn account_seed<'a, P: Into<Cow<'a, str>>>(
|
||||
&self,
|
||||
bip39_password: P,
|
||||
) -> Zeroizing<[u8; 64]> {
|
||||
Zeroizing::new(self.secret.to_seed(bip39_password))
|
||||
}
|
||||
|
||||
/// Derive an extended private key from the stored account secret assuming no bip39 password
|
||||
#[deprecated(
|
||||
note = "use derive_extended_private_key_with_password to ensure correct derivation if used bip39 password"
|
||||
)]
|
||||
pub fn derive_extended_private_key(
|
||||
&self,
|
||||
hd_path: &DerivationPath,
|
||||
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
|
||||
let seed = self.account_seed("");
|
||||
derive_extended_private_key(seed, hd_path)
|
||||
}
|
||||
|
||||
pub fn derive_keypair<'a, P: Into<Cow<'a, str>>>(
|
||||
&self,
|
||||
hd_path: &DerivationPath,
|
||||
bip39_password: P,
|
||||
) -> Result<Secp256k1Keypair, DirectSecp256k1HdWalletError> {
|
||||
let seed = self.account_seed(bip39_password);
|
||||
derive_keypair(seed, hd_path)
|
||||
}
|
||||
|
||||
pub fn derive_extended_private_key_with_password<'a, P: Into<Cow<'a, str>>>(
|
||||
&self,
|
||||
hd_path: &DerivationPath,
|
||||
bip39_password: P,
|
||||
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
|
||||
let seed = self.account_seed(bip39_password);
|
||||
derive_extended_private_key(seed, hd_path)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -188,23 +191,39 @@ impl DirectSecp256k1HdWalletBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
note = "this function can potentially panic if accounts can't be derived correctly. please use .try_build() instead"
|
||||
)]
|
||||
pub fn build(self, mnemonic: bip39::Mnemonic) -> DirectSecp256k1HdWallet {
|
||||
let seed = mnemonic.to_seed(&self.bip39_password);
|
||||
// unfortunately due to backwards compatibility requirements,
|
||||
// we can't change signature of this method
|
||||
#[allow(clippy::expect_used)]
|
||||
self.try_build(mnemonic)
|
||||
.expect("account derivation failure")
|
||||
}
|
||||
|
||||
pub fn try_build(
|
||||
self,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
) -> Result<DirectSecp256k1HdWallet, DirectSecp256k1HdWalletError> {
|
||||
let seed = Zeroizing::new(mnemonic.to_seed(&self.bip39_password));
|
||||
let prefix = self.prefix.clone();
|
||||
let accounts = self
|
||||
.hd_paths
|
||||
.iter()
|
||||
.map(|hd_path| Secp256k1Derivation {
|
||||
hd_path: hd_path.clone(),
|
||||
prefix: prefix.clone(),
|
||||
.map(|hd_path| {
|
||||
Secp256k1Derivation {
|
||||
hd_path: hd_path.clone(),
|
||||
prefix: prefix.clone(),
|
||||
}
|
||||
.try_derive_account(&seed)
|
||||
})
|
||||
.collect();
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
DirectSecp256k1HdWallet {
|
||||
Ok(DirectSecp256k1HdWallet {
|
||||
accounts,
|
||||
seed,
|
||||
secret: mnemonic,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +234,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn generating_account_addresses() {
|
||||
fn generating_account_addresses() -> anyhow::Result<()> {
|
||||
// test vectors produced from our js wallet
|
||||
let mnemonics = ["crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove",
|
||||
"acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel",
|
||||
@@ -230,11 +249,10 @@ mod tests {
|
||||
"n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf",
|
||||
];
|
||||
for (idx, mnemonic) in mnemonics.iter().enumerate() {
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(&prefix, mnemonic.parse().unwrap());
|
||||
assert_eq!(
|
||||
wallet.try_derive_accounts().unwrap()[0].address,
|
||||
addrs[idx].parse().unwrap()
|
||||
)
|
||||
let wallet =
|
||||
DirectSecp256k1HdWallet::checked_from_mnemonic(&prefix, mnemonic.parse()?)?;
|
||||
assert_eq!(wallet.signer_addresses()[0], addrs[idx].parse().unwrap());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::signing::direct_wallet::DirectSecp256k1HdWalletError;
|
||||
use bip32::XPrv;
|
||||
use cosmrs::bip32::DerivationPath;
|
||||
use cosmrs::crypto::secp256k1::SigningKey;
|
||||
use cosmrs::crypto::PublicKey;
|
||||
@@ -12,14 +14,64 @@ pub mod direct_wallet;
|
||||
pub mod signer;
|
||||
pub mod tx_signer;
|
||||
|
||||
pub(crate) type Secp256k1Keypair = (SigningKey, PublicKey);
|
||||
|
||||
/// Derivation information required to derive a keypair and an address from a mnemonic.
|
||||
#[derive(Debug, Clone)]
|
||||
struct Secp256k1Derivation {
|
||||
pub(crate) struct Secp256k1Derivation {
|
||||
hd_path: DerivationPath,
|
||||
prefix: String,
|
||||
}
|
||||
|
||||
// TODO: is this struct going to be derivable with other signer types?
|
||||
impl Secp256k1Derivation {
|
||||
pub(crate) fn try_derive_account<S>(
|
||||
&self,
|
||||
seed: S,
|
||||
) -> Result<AccountData, DirectSecp256k1HdWalletError>
|
||||
where
|
||||
S: AsRef<[u8]>,
|
||||
{
|
||||
let keypair = derive_keypair(seed, &self.hd_path)?;
|
||||
|
||||
// it seems this can only fail if the provided account prefix is invalid
|
||||
let address = keypair
|
||||
.1
|
||||
.account_id(&self.prefix)
|
||||
.map_err(|source| DirectSecp256k1HdWalletError::AccountDerivationError { source })?;
|
||||
|
||||
Ok(AccountData {
|
||||
address,
|
||||
public_key: keypair.1,
|
||||
private_key: keypair.0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derive_keypair<S>(
|
||||
seed: S,
|
||||
hd_path: &DerivationPath,
|
||||
) -> Result<Secp256k1Keypair, DirectSecp256k1HdWalletError>
|
||||
where
|
||||
S: AsRef<[u8]>,
|
||||
{
|
||||
let extended_private_key = derive_extended_private_key(seed, hd_path)?;
|
||||
|
||||
let private_key: SigningKey = extended_private_key.into();
|
||||
let public_key = private_key.public_key();
|
||||
|
||||
Ok((private_key, public_key))
|
||||
}
|
||||
|
||||
pub fn derive_extended_private_key<S>(
|
||||
seed: S,
|
||||
hd_path: &DerivationPath,
|
||||
) -> Result<XPrv, DirectSecp256k1HdWalletError>
|
||||
where
|
||||
S: AsRef<[u8]>,
|
||||
{
|
||||
Ok(XPrv::derive_from_path(seed, hd_path)?)
|
||||
}
|
||||
|
||||
pub struct AccountData {
|
||||
pub address: AccountId,
|
||||
|
||||
|
||||
@@ -33,23 +33,18 @@ pub enum SignerType {
|
||||
pub trait OfflineSigner {
|
||||
type Error: From<SigningError>;
|
||||
|
||||
// I really dislike existence of this function because it makes you re-derive your key **twice** for each contract transaction
|
||||
fn signer_addresses(&self) -> Result<Vec<AccountId>, Self::Error> {
|
||||
let derived_addresses = self
|
||||
.get_accounts()?
|
||||
.into_iter()
|
||||
.map(|account| account.address)
|
||||
.collect();
|
||||
Ok(derived_addresses)
|
||||
fn signer_addresses(&self) -> Vec<AccountId> {
|
||||
self.get_accounts()
|
||||
.iter()
|
||||
.map(|account| account.address.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error>;
|
||||
fn get_accounts(&self) -> &[AccountData];
|
||||
|
||||
fn find_account(&self, signer_address: &AccountId) -> Result<AccountData, Self::Error> {
|
||||
// TODO: we could really use some zeroize action here
|
||||
let accounts = self.get_accounts()?;
|
||||
accounts
|
||||
.into_iter()
|
||||
fn find_account(&self, signer_address: &AccountId) -> Result<&AccountData, Self::Error> {
|
||||
self.get_accounts()
|
||||
.iter()
|
||||
.find(|account| &account.address == signer_address)
|
||||
.ok_or_else(|| {
|
||||
SigningError::AccountNotFound {
|
||||
@@ -76,7 +71,7 @@ pub trait OfflineSigner {
|
||||
message: M,
|
||||
) -> Result<Signature, Self::Error> {
|
||||
let signer = self.find_account(signer_address)?;
|
||||
self.sign_raw_with_account(&signer, message)
|
||||
self.sign_raw_with_account(signer, message)
|
||||
}
|
||||
|
||||
fn sign_direct(
|
||||
@@ -85,7 +80,7 @@ pub trait OfflineSigner {
|
||||
sign_doc: SignDoc,
|
||||
) -> Result<tx::Raw, Self::Error> {
|
||||
let signer = self.find_account(signer_address)?;
|
||||
self.sign_direct_with_account(&signer, sign_doc)
|
||||
self.sign_direct_with_account(signer, sign_doc)
|
||||
}
|
||||
|
||||
// unless explicitly defined, each signing method is unsupported
|
||||
@@ -122,7 +117,7 @@ pub struct NoSigner;
|
||||
// impl OfflineSigner for NoSigner {
|
||||
// type Error = SignerUnavailable;
|
||||
//
|
||||
// fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
|
||||
// fn get_accounts(&self) -> &[AccountData] {
|
||||
// return Err(SignerUnavailable);
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -50,7 +50,7 @@ pub trait TxSigner: OfflineSigner {
|
||||
)
|
||||
.map_err(|source| SigningError::SignDocFailure { source })?;
|
||||
|
||||
self.sign_direct_with_account(&account_from_signer, sign_doc)
|
||||
self.sign_direct_with_account(account_from_signer, sign_doc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use clap::Parser;
|
||||
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
use nym_validator_client::signing::signer::OfflineSigner;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -15,9 +16,10 @@ pub fn create_account(args: Args, prefix: &str) {
|
||||
let word_count = args.word_count.unwrap_or(24);
|
||||
let mnemonic = bip39::Mnemonic::generate(word_count).expect("failed to generate mnemonic!");
|
||||
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||
let wallet = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic)
|
||||
.expect("failed to derive accounts!");
|
||||
|
||||
// Output address and mnemonics into separate lines for easier parsing
|
||||
println!("{}", wallet.mnemonic_string().as_str());
|
||||
println!("{}", wallet.try_derive_accounts().unwrap()[0].address());
|
||||
println!("{}", wallet.signer_addresses()[0]);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::QueryClient;
|
||||
use crate::utils::show_error;
|
||||
use clap::Parser;
|
||||
use log::{error, info};
|
||||
use nym_validator_client::nyxd::{AccountId, CosmWasmClient};
|
||||
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
|
||||
use crate::context::QueryClient;
|
||||
use crate::utils::show_error;
|
||||
use nym_validator_client::signing::signer::OfflineSigner;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -50,20 +50,19 @@ pub async fn get_pubkey(
|
||||
}
|
||||
|
||||
pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip39::Mnemonic) {
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||
match wallet.try_derive_accounts() {
|
||||
Ok(accounts) => match accounts.iter().find(|a| *a.address() == address) {
|
||||
Some(account) => {
|
||||
println!("{}", account.public_key().to_string());
|
||||
}
|
||||
None => {
|
||||
error!("Could not derive key that matches {address}")
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to derive accounts. {e}");
|
||||
let wallet = match DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic) {
|
||||
Ok(wallet) => wallet,
|
||||
Err(err) => {
|
||||
error!("Failed to derive accounts. {err}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let Ok(account) = wallet.find_account(&address) else {
|
||||
error!("Could not derive key that matches {address}");
|
||||
return;
|
||||
};
|
||||
println!("{}", account.public_key().to_string());
|
||||
}
|
||||
|
||||
pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) {
|
||||
|
||||
@@ -36,32 +36,35 @@ pub fn sign(args: Args, prefix: &str, mnemonic: Option<bip39::Mnemonic>) {
|
||||
return;
|
||||
}
|
||||
|
||||
let wallet =
|
||||
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.expect("mnemonic not set"));
|
||||
match wallet.try_derive_accounts() {
|
||||
Ok(accounts) => match accounts.first() {
|
||||
Some(account) => {
|
||||
let msg = args.message.into_bytes();
|
||||
match wallet.sign_raw_with_account(account, msg) {
|
||||
Ok(signature) => {
|
||||
let output = SignatureOutputJson {
|
||||
account_id: account.address().to_string(),
|
||||
public_key: account.public_key(),
|
||||
signature_as_hex: signature.to_string(),
|
||||
};
|
||||
println!("{}", json!(output));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to sign message. {e}");
|
||||
}
|
||||
let wallet = match DirectSecp256k1HdWallet::checked_from_mnemonic(
|
||||
prefix,
|
||||
mnemonic.expect("mnemonic not set"),
|
||||
) {
|
||||
Ok(wallet) => wallet,
|
||||
Err(err) => {
|
||||
error!("Could not derive an account key from the mnemonic: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
match wallet.get_accounts().first() {
|
||||
Some(account) => {
|
||||
let msg = args.message.into_bytes();
|
||||
match wallet.sign_raw_with_account(account, msg) {
|
||||
Ok(signature) => {
|
||||
let output = SignatureOutputJson {
|
||||
account_id: account.address().to_string(),
|
||||
public_key: account.public_key(),
|
||||
signature_as_hex: signature.to_string(),
|
||||
};
|
||||
println!("{}", json!(output));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to sign message. {e}");
|
||||
}
|
||||
}
|
||||
None => {
|
||||
error!("Could not derive an account key from the mnemonic",)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to derive accounts. {e}");
|
||||
}
|
||||
None => {
|
||||
error!("Could not derive an account key from the mnemonic",)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,23 +241,28 @@ impl Epoch {
|
||||
//
|
||||
// Note: It's important that the variant ordering is not changed otherwise it would mess up the derived `PartialOrd`
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
#[derive(Copy, Default)]
|
||||
pub enum EpochState {
|
||||
#[default]
|
||||
WaitingInitialisation,
|
||||
PublicKeySubmission { resharing: bool },
|
||||
DealingExchange { resharing: bool },
|
||||
VerificationKeySubmission { resharing: bool },
|
||||
VerificationKeyValidation { resharing: bool },
|
||||
VerificationKeyFinalization { resharing: bool },
|
||||
PublicKeySubmission {
|
||||
resharing: bool,
|
||||
},
|
||||
DealingExchange {
|
||||
resharing: bool,
|
||||
},
|
||||
VerificationKeySubmission {
|
||||
resharing: bool,
|
||||
},
|
||||
VerificationKeyValidation {
|
||||
resharing: bool,
|
||||
},
|
||||
VerificationKeyFinalization {
|
||||
resharing: bool,
|
||||
},
|
||||
InProgress,
|
||||
}
|
||||
|
||||
impl Default for EpochState {
|
||||
fn default() -> Self {
|
||||
Self::WaitingInitialisation
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EpochState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
||||
@@ -8,7 +8,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
use sqlx::{Connection, SqliteConnection};
|
||||
use std::env;
|
||||
|
||||
let out_dir = env::var("OUT_DIR")?;
|
||||
let out_dir = env::var("OUT_DIR").context("missing OUT_DIR env variable")?;
|
||||
let database_path = format!("{out_dir}/nym-credential-proxy-example.sqlite");
|
||||
|
||||
// remove the db file if it already existed from previous build
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user