Compare commits

..

51 Commits

Author SHA1 Message Date
Yana 28b4fe7e7e add 10 recommended nodes 2025-06-05 12:33:43 +03:00
Yana 9479d2a383 Add recommended nodes 2025-06-04 19:47:53 +03:00
Yana 886b4410aa Fix open in new tab click on NodeTable 2025-06-03 14:28:17 +03:00
Yana b51358fb12 Style fixes 2025-05-22 14:24:24 +03:00
Yana 53e3acaa37 Add countries and locations to WorldMap 2025-05-21 17:11:52 +03:00
Yana 978817baf7 fix build 2025-05-15 19:20:16 +03:00
Yana 9319a5ec04 fix self-bond, redirect articles to nym/blog 2025-05-15 19:15:29 +03:00
Yana 3186db2915 style fixes 2025-05-14 20:47:26 +03:00
Yana ff7671f28a update copy 2025-05-14 20:38:07 +03:00
Yana cbe8eec2a4 fix dark mode font color 2025-05-14 19:53:07 +03:00
Yana 42f9edd408 Add self-bond and operating costs to NodeTable 2025-05-14 19:40:31 +03:00
Yana 128cf7c070 Add colors on uptime 2025-05-09 15:46:50 +03:00
Yana 79e5004849 revamp NodeTable 2025-05-09 15:27:54 +03:00
Yana 0d6722f9f5 'Change footer version to 2.2 2025-05-08 15:17:28 +03:00
Yana d458df9c34 fix build 2025-05-08 15:08:48 +03:00
Yana 7a8ac59a36 Add default sorting by country to Node tables 2025-05-08 14:56:04 +03:00
Yana ad3eb7a84c fix build 2025-05-07 19:54:09 +03:00
Yana 135f248eba Replace spectreDao delegations 2025-05-07 18:59:05 +03:00
Yana 7012bf9886 Add node count on every quick filter 2025-05-06 16:25:40 +03:00
Yana 88aa32ddeb Fix advanced filtering UI 2025-05-06 16:15:23 +03:00
Yana 7c1c9976f0 fix build 2025-05-04 19:27:47 +03:00
Yana 4ee7f7eaf5 Fix saturation filter 2025-05-04 19:23:35 +03:00
Yana 778772d96a fix build 2025-05-04 19:16:30 +03:00
Yana 5b791b41aa Add advanced filters 2025-05-04 19:13:34 +03:00
Yana 4b7e51fc3b Add quick filters on NodeTable 2025-05-04 11:27:29 +03:00
Yana 0a42dd3e0d fix mobile map 2025-04-22 20:20:44 +03:00
Yana 7cf49f642d fix images 2025-04-22 19:47:40 +03:00
Yana 089ab65dd7 Fix maps 2025-04-22 18:51:29 +03:00
Yana c1fabae770 Clean up 2025-04-17 18:25:43 +03:00
Yana 3ed7cfa381 Replace SpectreDao on AccountPageButtonGroup 2025-04-17 18:21:30 +03:00
Yana 4fe83da99d Replace SpectreDao api in Staking Table 2025-04-17 18:16:13 +03:00
Yana 4f81fc7400 Replace SpectreDao api on Magic Search 2025-04-17 17:55:52 +03:00
Yana 6d601ca654 Replace SpectreDao api on Stakers Card 2025-04-17 17:46:35 +03:00
Yana cea3ad9908 Add dark mode on error cards 2025-04-17 17:36:27 +03:00
Yana e4ecd099cc Add dark mode on error cards 2025-04-17 17:28:08 +03:00
Yana 0723542c39 clean up 2025-04-16 21:20:14 +03:00
Yana 523e559ff8 clean up 2025-04-16 21:17:15 +03:00
Yana 02b27573de clean up 2025-04-16 21:08:31 +03:00
Yana 8f229737a3 Replace SpectreDao on NodeTable and Node page 2025-04-16 21:06:12 +03:00
Yana 1afd13d6e0 Clean up 2025-04-16 15:27:53 +03:00
Yana df10b5595a Add styles 2025-04-16 15:23:05 +03:00
Yana 443031ba66 test data fetching 2025-04-16 13:37:35 +03:00
Yana 8d340a49d3 fix data fetching 2025-04-16 09:57:27 +03:00
Yana e0925d3c7f clean up 2025-04-16 08:40:34 +03:00
Yana 89d391da29 fix build 2025-04-16 08:13:21 +03:00
Yana cc2d7d34d2 reset last changes 2025-04-16 08:05:04 +03:00
Yana 969070f938 fix build, fix map sizes 2025-04-15 21:38:05 +03:00
Yana 3dfcae9369 fix build 2025-04-15 21:04:58 +03:00
Yana 32a4bf1172 fix build 2025-04-15 20:54:37 +03:00
Yana 433cac8c58 Fix map sizing 2025-04-15 18:15:00 +03:00
Yana 4fc64a072c Add WorldMap 2025-04-15 16:47:37 +03:00
1531 changed files with 138106 additions and 55581 deletions
+11 -1
View File
@@ -14,6 +14,7 @@
# contracts
/contracts/mixnet @durch @jstuczyn
/contracts/vesting @durch @jstuczyn
/contracts/service-provider-directory @octol
# crypto code
/common/crypto/ @jstuczyn
@@ -21,5 +22,14 @@
/common/dkg/ @jstuczyn
/common/nymsphinx/ @jstuczyn
# rust sdk
/sdk/rust/ @octol
# nym-connect (rust)
/nym-connect/desktop/src-tauri/ @octol
# nym-wallet (rust)
/nym-wallet/src-tauri/ @octol
# documentation
/documentation @mfahampshire
/documentation @mfahampshire
+3 -3
View File
@@ -415,9 +415,9 @@
}
},
"node_modules/undici": {
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"version": "5.28.5",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
"integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
"license": "MIT",
"dependencies": {
"@fastify/busboy": "^2.0.0"
+2
View File
@@ -31,3 +31,5 @@ updates:
update-types:
- "patch"
open-pull-requests-limit: 10
assignees:
- "octol"
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
run: sudo apt-get install -y rsync
- uses: rlespinasse/github-slug-action@v3.x
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
uses: pnpm/action-setup@v4.0.0
with:
version: 9
- uses: actions/setup-node@v4
+2 -2
View File
@@ -5,6 +5,7 @@ on:
paths:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'nym-api/**'
@@ -12,7 +13,6 @@ on:
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-statistics-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
- 'nyx-chain-watcher/**'
@@ -38,7 +38,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ arc-ubuntu-22.04, custom-windows-11, custom-macos-15 ]
os: [ arc-ubuntu-22.04, custom-windows-11, custom-runner-mac-m1 ]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v4
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -44,10 +44,8 @@ jobs:
echo "Tag is empty"
exit 1
fi
# first, list all tags for logging purposes
curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq
# check if there's a matching tag
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq -r --arg tag "$TAG" 'any(.tags[]; . == $tag)' )
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq --arg tag $TAG '.tags | contains([$tag])' )
if [[ $exists = "true" ]]; then
echo "Version '$TAG' defined in Cargo.toml ALREADY EXISTS as tag in harbor repo"
exit 1
@@ -55,5 +53,5 @@ jobs:
echo "Version '$TAG' doesn't exist on the remote"
else
echo "Unknown output '$exists'"
exit 2
exit 1
fi
@@ -1,59 +0,0 @@
name: ci-check-nym-stats-api-version
on:
pull_request:
paths:
- "nym-statistics-api/**"
env:
WORKING_DIRECTORY: "nym-statistics-api"
jobs:
check-if-tag-exists:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Check if git tag exists
run: |
TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
if [[ -z "$TAG" ]]; then
echo "Tag is empty"
exit 1
fi
git ls-remote --tags origin | awk '{print $2}'
if git ls-remote --tags origin | awk '{print $2}' | grep -q "refs/tags/$TAG$" ; then
echo "Tag '$TAG' ALREADY EXISTS on the remote"
exit 1
else
echo "Tag '$TAG' does not exist on the remote"
fi
- name: Check if harbor tag exists
run: |
TAG=${{ steps.get_version.outputs.result }}
registry=https://harbor.nymte.ch
repo_name=nym/nym-statistics-api
if [[ -z $TAG ]]; then
echo "Tag is empty"
exit 1
fi
# first, list all tags for logging purposes
curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq
# check if there's a matching tag
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq -r --arg tag "$TAG" 'any(.tags[]; . == $tag)' )
if [[ $exists = "true" ]]; then
echo "Version '$TAG' defined in Cargo.toml ALREADY EXISTS as tag in harbor repo"
exit 1
elif [[ $exists = "false" ]]; then
echo "Version '$TAG' doesn't exist on the remote"
else
echo "Unknown output '$exists'"
exit 2
fi
@@ -56,8 +56,6 @@ jobs:
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ecash.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_pool_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_performance_contract.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
+1 -3
View File
@@ -20,7 +20,6 @@ jobs:
runs-on: ubuntu-22.04
env:
CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
- uses: actions/checkout@v4
@@ -28,8 +27,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
# pinned due to issues building contracts
toolchain: 1.86.0
toolchain: stable
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
run: sudo apt-get install -y rsync
- uses: rlespinasse/github-slug-action@v3.x
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
uses: pnpm/action-setup@v4.0.0
with:
version: 9
- uses: actions/setup-node@v4
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
+7 -7
View File
@@ -20,10 +20,8 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- os: arc-ubuntu-22.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
platform: [custom-ubuntu-22.04]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
@@ -56,7 +54,7 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.86.0
toolchain: stable
override: true
- name: Build all binaries
@@ -70,6 +68,7 @@ jobs:
with:
name: my-artifact
path: |
target/release/explorer-api
target/release/nym-client
target/release/nym-socks5-client
target/release/nym-api
@@ -78,13 +77,14 @@ jobs:
target/release/nymvisor
target/release/nym-node
retention-days: 30
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
uses: softprops/action-gh-release@v2
if: github.event_name == 'release'
with:
files: |
target/release/explorer-api
target/release/nym-client
target/release/nym-socks5-client
target/release/nym-api
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
@@ -31,7 +31,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
-51
View File
@@ -1,51 +0,0 @@
name: Build and upload Nym APU container to harbor.nymte.ch
on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "."
CONTAINER_NAME: "nym-api"
jobs:
build-container:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.nymte.ch
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure git identity
run: |
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-api/Cargo.toml
- name: Remove existing tag if exists
run: |
echo "Checking if tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} exists..."
if git rev-parse ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
echo "Tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} already exists"
git push --delete origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
git tag -d ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
fi
- name: Create tag
run: |
git tag -a ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
git push origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f nym-api.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -1,42 +0,0 @@
name: Build and upload Nym Statistics API container to harbor.nymte.ch
on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "nym-statistics-api"
CONTAINER_NAME: "nym-statistics-api"
jobs:
build-container:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.nymte.ch
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure git identity
run: |
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Create tag
run: |
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.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.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -3
View File
@@ -40,6 +40,7 @@ validator-config
validator-api-config.toml
dist
storybook-static
envs/qwerty.env
.parcel-cache
**/.DS_Store
cpu-cycles/libcpucycles/build
@@ -58,6 +59,3 @@ nym-api/redocly/formatted-openapi.json
*.sqlite
.build
**/settings.sql
**/enter_db.sh
-150
View File
@@ -4,156 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.11-cheddar] (2025-06-10)
- No autoremoval of peers ([#5831])
- Set cached storage counters to 0 ([#5812])
- hack: temporarily use next.config.js instead of next.config.ts ([#5805])
- chore: resolve 1.87 clippy warnings ([#5802])
- Nym Statistics API ([#5800])
- QoL: RequestPath trait for http-api-client ([#5788])
- Fix contains ticketbook function that always returned true ([#5787])
- swap a decode into a fromrow to please future postgres feature ([#5785])
- Make address cache configurable ([#5784])
- Track wireguard credential retries ([#5783])
[#5831]: https://github.com/nymtech/nym/pull/5831
[#5812]: https://github.com/nymtech/nym/pull/5812
[#5805]: https://github.com/nymtech/nym/pull/5805
[#5802]: https://github.com/nymtech/nym/pull/5802
[#5800]: https://github.com/nymtech/nym/pull/5800
[#5788]: https://github.com/nymtech/nym/pull/5788
[#5787]: https://github.com/nymtech/nym/pull/5787
[#5785]: https://github.com/nymtech/nym/pull/5785
[#5784]: https://github.com/nymtech/nym/pull/5784
[#5783]: https://github.com/nymtech/nym/pull/5783
## [2025.10-brie] (2025-05-27)
- Backport PR 5779 ([#5801])
- Expanded Accept Encoding for `reqwest` ([#5779])
- Teach HttpClientError how to report its status code and timeout ([#5770])
- Skip refreshing the topology on startup as we already have an initial set ([#5768])
- Fetch the topology from the nym-api concurrently ([#5767])
- feat: use bincode by default in NymApiClient + remove feature-lock ([#5761])
- Instrument create_request ([#5760])
- Add node_bonded field to delegations ([#5759])
- build(deps): bump mikefarah/yq from 4.45.1 to 4.45.4 ([#5758])
- Raw route submissions ([#5756])
- feat: expires header for `/active` nym-api responses ([#5755])
- Decrease default average packet delay to 15 ms ([#5754])
- build(deps): bump the patch-updates group across 1 directory with 12 updates ([#5753])
- Remove pretty_env_logger and switch remaining crates to use tracing ([#5749])
- Update pretty_env_logger to latest to not depend on unmaintained crate atty ([#5748])
- Upgrade prometheus crate to fix security warning ([#5747])
- Downgrade deranged crate to 0.4.0 ([#5746])
- feat: nym-api bincode + yaml support ([#5745])
- fix parallel feature in ecash crate with send + sync ([#5744])
- Remove old test directory - Update validator docker ([#5743])
- [Feature] `RememberMe` is the new don't `ForgetMe` ([#5742])
- build(deps): bump ammonia from 4.0.0 to 4.1.0 ([#5739])
- build(deps): bump base-x from 3.0.9 to 3.0.11 in /testnet-faucet ([#5737])
- build(deps): bump http-proxy-middleware from 2.0.8 to 2.0.9 ([#5730])
[#5801]: https://github.com/nymtech/nym/pull/5801
[#5779]: https://github.com/nymtech/nym/pull/5779
[#5770]: https://github.com/nymtech/nym/pull/5770
[#5768]: https://github.com/nymtech/nym/pull/5768
[#5767]: https://github.com/nymtech/nym/pull/5767
[#5761]: https://github.com/nymtech/nym/pull/5761
[#5760]: https://github.com/nymtech/nym/pull/5760
[#5759]: https://github.com/nymtech/nym/pull/5759
[#5758]: https://github.com/nymtech/nym/pull/5758
[#5756]: https://github.com/nymtech/nym/pull/5756
[#5755]: https://github.com/nymtech/nym/pull/5755
[#5754]: https://github.com/nymtech/nym/pull/5754
[#5753]: https://github.com/nymtech/nym/pull/5753
[#5749]: https://github.com/nymtech/nym/pull/5749
[#5748]: https://github.com/nymtech/nym/pull/5748
[#5747]: https://github.com/nymtech/nym/pull/5747
[#5746]: https://github.com/nymtech/nym/pull/5746
[#5745]: https://github.com/nymtech/nym/pull/5745
[#5744]: https://github.com/nymtech/nym/pull/5744
[#5743]: https://github.com/nymtech/nym/pull/5743
[#5742]: https://github.com/nymtech/nym/pull/5742
[#5739]: https://github.com/nymtech/nym/pull/5739
[#5737]: https://github.com/nymtech/nym/pull/5737
[#5730]: https://github.com/nymtech/nym/pull/5730
## [2025.9-appenzeller] (2025-05-13)
- build(deps): bump clap from 4.5.36 to 4.5.37 in the patch-updates group ([#5722])
- build(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 in /wasm/mix-fetch/go-mix-conn ([#5720])
- build(deps-dev): bump http-proxy-middleware from 2.0.6 to 2.0.9 in /wasm/client/internal-dev ([#5719])
- Add /account/{address} ([#5673])
- Add contains ticketbook data db query ([#5670])
[#5722]: https://github.com/nymtech/nym/pull/5722
[#5720]: https://github.com/nymtech/nym/pull/5720
[#5719]: https://github.com/nymtech/nym/pull/5719
[#5673]: https://github.com/nymtech/nym/pull/5673
[#5670]: https://github.com/nymtech/nym/pull/5670
## [2025.8-tourist] (2025-04-29)
- add reserved byte to reply surb serialisation ([#5731])
- Remove inactive peers ([#5721])
- Update Hickory DNS "0.24.4" to "0.25" ([#5709])
- build(deps): bump the patch-updates group across 1 directory with 7 updates ([#5708])
- Peer handle should die more gracefully ([#5704])
- build(deps): bump crossbeam-channel from 0.5.14 to 0.5.15 ([#5702])
- build(deps): bump actions/checkout from 3 to 4 ([#5700])
- Feature/updated sphinx payload keys ([#5698])
- Bump the nym-vpn deb metapackage to 1.0 ([#5697])
- Make mix hops optional for Mixnet Client ([#5696])
- build(deps): bump tokio from 1.44.1 to 1.44.2 ([#5693])
- Feature/replay protection ([#5682])
- Adding fresh nym-api tests and workflow ([#5659])
- build(deps): bump next from 14.2.21 to 14.2.25 ([#5655])
- build(deps): bump pnpm/action-setup from 4.0.0 to 4.1.0 ([#5436])
[#5731]: https://github.com/nymtech/nym/pull/5731
[#5721]: https://github.com/nymtech/nym/pull/5721
[#5709]: https://github.com/nymtech/nym/pull/5709
[#5708]: https://github.com/nymtech/nym/pull/5708
[#5704]: https://github.com/nymtech/nym/pull/5704
[#5702]: https://github.com/nymtech/nym/pull/5702
[#5700]: https://github.com/nymtech/nym/pull/5700
[#5698]: https://github.com/nymtech/nym/pull/5698
[#5697]: https://github.com/nymtech/nym/pull/5697
[#5696]: https://github.com/nymtech/nym/pull/5696
[#5693]: https://github.com/nymtech/nym/pull/5693
[#5682]: https://github.com/nymtech/nym/pull/5682
[#5659]: https://github.com/nymtech/nym/pull/5659
[#5655]: https://github.com/nymtech/nym/pull/5655
[#5436]: https://github.com/nymtech/nym/pull/5436
## [2025.7-tex] (2025-04-14)
- Expand /v3/nym-nodes with geodata ([#5686])
- chore: clippy for 1.86 ([#5685])
- Featrure: Bash scripts to init and configure VMs conveniently and update docs ([#5681])
- Update node versions in CI ([#5677])
- build(deps): bump the patch-updates group across 1 directory with 8 updates ([#5668])
- Update log crate ([#5667])
- Minor fixes involving key cloning and hashing ([#5664])
- mix throughput tester ([#5661])
- build(deps): bump blake3 from 1.6.1 to 1.7.0 ([#5658])
- build(deps): bump elliptic from 6.5.5 to 6.6.1 ([#5483])
- Move all workflows on ubuntu-20 to ubuntu-22 ([#5455])
[#5686]: https://github.com/nymtech/nym/pull/5686
[#5685]: https://github.com/nymtech/nym/pull/5685
[#5681]: https://github.com/nymtech/nym/pull/5681
[#5677]: https://github.com/nymtech/nym/pull/5677
[#5668]: https://github.com/nymtech/nym/pull/5668
[#5667]: https://github.com/nymtech/nym/pull/5667
[#5664]: https://github.com/nymtech/nym/pull/5664
[#5661]: https://github.com/nymtech/nym/pull/5661
[#5658]: https://github.com/nymtech/nym/pull/5658
[#5483]: https://github.com/nymtech/nym/pull/5483
[#5455]: https://github.com/nymtech/nym/pull/5455
## [2025.6-chuckles] (2025-03-31)
- Remove Google public DNS ([#5660])
Generated
+850 -1908
View File
File diff suppressed because it is too large Load Diff
+34 -34
View File
@@ -33,14 +33,11 @@ members = [
"common/commands",
"common/config",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/contracts-common-testing",
"common/cosmwasm-smart-contracts/easy_addr",
"common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/easy_addr",
"common/cosmwasm-smart-contracts/ecash-contract",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/nym-performance-contract",
"common/cosmwasm-smart-contracts/nym-pool-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
"common/credential-utils",
@@ -67,8 +64,6 @@ members = [
"common/nym-id",
"common/nym-metrics",
"common/nym_offline_compact_ecash",
"common/nymnoise",
"common/nymnoise/keys",
"common/nymsphinx",
"common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing",
@@ -102,6 +97,7 @@ members = [
"common/wireguard-types",
"documentation/autodoc",
"gateway",
"integrations/bity",
"nym-api",
"nym-api/nym-api-requests",
"nym-browser-extension/storage",
@@ -116,7 +112,6 @@ members = [
"nym-node/nym-node-metrics",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-statistics-api",
"nym-validator-rewarder",
"nyx-chain-watcher",
"sdk/ffi/cpp",
@@ -127,7 +122,7 @@ members = [
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"sqlx-pool-guard",
"tools/echo-server",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
@@ -137,7 +132,7 @@ members = [
"tools/internal/testnet-manager",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/internal/validator-status-check",
"tools/internal/testnet-manager/dkg-bypass-contract", "tools/internal/validator-status-check",
"tools/nym-cli",
"tools/nym-id-cli",
"tools/nym-nr-query",
@@ -158,7 +153,6 @@ default-members = [
"nym-node",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-statistics-api",
"nym-validator-rewarder",
"nyx-chain-watcher",
"service-providers/authenticator",
@@ -167,7 +161,12 @@ default-members = [
"tools/nymvisor",
]
exclude = ["explorer", "contracts", "nym-wallet", "cpu-cycles"]
exclude = [
"explorer",
"contracts",
"nym-wallet",
"cpu-cycles",
]
[workspace.package]
authors = ["Nym Technologies SA"]
@@ -186,7 +185,7 @@ aes = "0.8.1"
aes-gcm = "0.10.1"
aes-gcm-siv = "0.11.1"
ammonia = "4"
anyhow = "1.0.98"
anyhow = "1.0.97"
arc-swap = "1.7.1"
argon2 = "0.5.0"
async-trait = "0.1.88"
@@ -205,14 +204,14 @@ bloomfilter = "3.0.1"
bs58 = "0.5.1"
bytecodec = "0.4.15"
bytes = "1.10.1"
cargo_metadata = "0.19.2"
cargo_metadata = "0.18.1"
celes = "2.6.0"
cfg-if = "1.0.0"
chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1"
chrono = "0.4.41"
chrono = "0.4.40"
cipher = "0.4.3"
clap = "4.5.38"
clap = "4.5.34"
clap_complete = "4.5"
clap_complete_fig = "4.5"
colored = "2.2"
@@ -237,12 +236,12 @@ dotenvy = "0.15.6"
ecdsa = "0.16"
ed25519-dalek = "2.1"
encoding_rs = "0.8.35"
env_logger = "0.11.8"
env_logger = "0.11.7"
envy = "0.4"
etherparse = "0.13.0"
eyre = "0.6.9"
fastrand = "2.1.1"
flate2 = "1.1.1"
flate2 = "1.1.0"
futures = "0.3.31"
futures-util = "0.3"
generic-array = "0.14.7"
@@ -252,7 +251,7 @@ handlebars = "3.5.5"
headers = "0.4.0"
hex = "0.4.3"
hex-literal = "0.3.3"
hickory-resolver = "0.25"
hickory-resolver = "0.24.4"
hkdf = "0.12.3"
hmac = "0.12.1"
http = "1"
@@ -287,8 +286,8 @@ pem = "0.8"
petgraph = "0.6.5"
pin-project = "1.1"
pin-project-lite = "0.2.16"
pretty_env_logger = "0.4.0"
publicsuffix = "2.3.0"
proc_pidinfo = "0.1.3"
quote = "1"
rand = "0.8.5"
rand_chacha = "0.3"
@@ -311,11 +310,10 @@ serde_json_path = "0.7.2"
serde_repr = "0.1"
serde_with = "3.9.0"
serde_yaml = "0.9.25"
sha2 = "0.10.9"
sha2 = "0.10.8"
si-scale = "0.2.3"
snow = "0.9.6"
sphinx-packet = "=0.6.0"
sqlx = "0.8.6"
sqlx = "0.7.4"
strum = "0.26"
strum_macros = "0.26"
subtle-encoding = "0.5"
@@ -323,17 +321,17 @@ syn = "1"
sysinfo = "0.33.0"
tap = "1.0.1"
tar = "0.4.44"
tempfile = "3.20"
tempfile = "3.19"
thiserror = "2.0"
time = "0.3.41"
tokio = "1.45"
tokio = "1.44"
tokio-postgres = "0.7"
tokio-stream = "0.1.17"
tokio-test = "0.4.4"
tokio-tun = "0.11.5"
tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.15"
toml = "0.8.22"
tokio-util = "0.7.14"
toml = "0.8.20"
tower = "0.5.2"
tower-http = "0.5.2"
tracing = "0.1.41"
@@ -344,7 +342,7 @@ tracing-tree = "0.2.2"
tracing-indicatif = "0.3.9"
ts-rs = "10.1.0"
tungstenite = { version = "0.20.1", default-features = false }
uniffi = "0.29.2"
uniffi = "0.29.1"
uniffi_build = "0.29.0"
url = "2.5"
utoipa = "5.2"
@@ -353,10 +351,11 @@ utoipauto = "0.2"
uuid = "*"
vergen = { version = "=8.3.1", default-features = false }
walkdir = "2"
wasm-bindgen-test = "0.3.49"
x25519-dalek = "2.0.0"
zeroize = "1.7.0"
prometheus = { version = "0.14.0" }
prometheus = { version = "0.13.0" }
# coconut/DKG related
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
@@ -370,6 +369,9 @@ subtle = "2.5.0"
# cosmwasm-related
cosmwasm-schema = "=2.2.2"
cosmwasm-std = "=2.2.2"
# use 1.0.1 as that's the version used by cosmwasm-std 2.2.1
# (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=1.0.1"
# same version as used by cosmwasm
cw-utils = "=2.0.0"
cw-storage-plus = "=2.0.0"
@@ -377,28 +379,26 @@ cw2 = { version = "=2.0.0" }
cw3 = { version = "=2.0.0" }
cw4 = { version = "=2.0.0" }
cw-controllers = { version = "=2.0.0" }
cw-multi-test = "=2.3.2"
# cosmrs-related
bip32 = { version = "0.5.3", default-features = false }
cosmrs = { version = "0.21.1" }
tendermint = "0.40.4"
tendermint-rpc = "0.40.4"
tendermint = "0.40.0"
tendermint-rpc = "0.40.0"
prost = { version = "0.13", default-features = false }
# wasm-related dependencies
gloo-utils = "0.2.0"
gloo-net = "0.6.0"
indexed_db_futures = "0.6.4"
indexed_db_futures = "0.6.1"
js-sys = "0.3.76"
serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5"
wasm-bindgen = "0.2.99"
wasm-bindgen-futures = "0.4.49"
wasm-bindgen-test = "0.3.49"
wasmtimer = "0.4.1"
web-sys = "0.3.76"
+1 -1
View File
@@ -133,7 +133,7 @@ clippy: sdk-wasm-lint
# Build contracts ready for deploy
# -----------------------------------------------------------------------------
CONTRACTS=vesting_contract mixnet_contract nym_ecash cw3_flex_multisig cw4_group nym_coconut_dkg nym_pool_contract nym_performance_contract
CONTRACTS=vesting_contract mixnet_contract nym_ecash cw3_flex_multisig cw4_group nym_coconut_dkg
CONTRACTS_WASM=$(addsuffix .wasm, $(CONTRACTS))
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.57"
version = "1.1.53"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -46,7 +46,6 @@ nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-bin-common = { path = "../../common/bin-common", features = [
"output_format",
"clap",
"basic_tracing",
] }
nym-client-core = { path = "../../common/client-core", features = [
"fs-credentials-storage",
@@ -2048,11 +2048,10 @@
}
},
"node_modules/http-proxy-middleware": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
"integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz",
"integrity": "sha512-m/4FxX17SUvz4lJ5WPXOHDUuCwIqXLfLHs1s0uZ3oYjhoXlx9csYxaOa0ElDEJ+h8Q4iJ1s+lTMbiCa4EXIJqg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.8",
"http-proxy": "^1.18.1",
@@ -6096,9 +6095,9 @@
}
},
"http-proxy-middleware": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
"integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz",
"integrity": "sha512-m/4FxX17SUvz4lJ5WPXOHDUuCwIqXLfLHs1s0uZ3oYjhoXlx9csYxaOa0ElDEJ+h8Q4iJ1s+lTMbiCa4EXIJqg==",
"dev": true,
"requires": {
"@types/http-proxy": "^1.17.8",
-1
View File
@@ -25,7 +25,6 @@ pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_33;
pub mod old_config_v1_1_54;
mod persistence;
mod template;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths;
use crate::client::config::{default_config_filepath, Socket, SocketType};
use crate::client::config::{default_config_filepath, Config, Socket, SocketType};
use crate::error::ClientError;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_33::CommonClientPathsV1_1_33;
@@ -14,8 +14,6 @@ use std::io;
use std::net::{IpAddr, Ipv4Addr};
use std::path::Path;
use super::old_config_v1_1_54::ConfigV1_1_54;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct ClientPathsV1_1_33 {
#[serde(flatten)]
@@ -35,21 +33,6 @@ pub struct ConfigV1_1_33 {
pub logging: LoggingSettings,
}
impl TryFrom<ConfigV1_1_33> for ConfigV1_1_54 {
type Error = ClientError;
fn try_from(value: ConfigV1_1_33) -> Result<Self, Self::Error> {
Ok(ConfigV1_1_54 {
base: value.base.into(),
socket: value.socket.into(),
storage_paths: ClientPaths {
common_paths: value.storage_paths.common_paths.upgrade_default()?,
},
logging: value.logging,
})
}
}
impl ConfigV1_1_33 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
@@ -58,6 +41,17 @@ impl ConfigV1_1_33 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, ClientError> {
Ok(Config {
base: self.base.into(),
socket: self.socket.into(),
storage_paths: ClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
})
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
@@ -1,41 +0,0 @@
use std::{io, path::Path};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::old_config_v1_1_54::ConfigV1_1_54 as BaseConfigV1_1_54;
use nym_config::read_config_from_toml_file;
use serde::{Deserialize, Serialize};
use crate::error::ClientError;
use super::{default_config_filepath, persistence::ClientPaths, Config, Socket};
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone)]
pub struct ConfigV1_1_54 {
#[serde(flatten)]
pub base: BaseConfigV1_1_54,
pub socket: Socket,
pub storage_paths: ClientPaths,
pub logging: LoggingSettings,
}
impl ConfigV1_1_54 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, ClientError> {
Ok(Config {
base: self.base.into(),
socket: self.socket,
storage_paths: self.storage_paths,
logging: self.logging,
})
}
}
@@ -92,6 +92,10 @@ host = '{{ socket.host }}'
[debug]
[debug.traffic]
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[debug.acknowledgements]
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
+4 -28
View File
@@ -5,7 +5,6 @@ use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::client::config::old_config_v1_1_54::ConfigV1_1_54;
use crate::client::config::{BaseClientConfig, Config};
use crate::commands::ecash::Ecash;
use crate::error::ClientError;
@@ -178,8 +177,7 @@ async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated_step3, gateway_config) = updated_step2.upgrade()?;
let old_paths = updated_step3.storage_paths.clone();
let updated_step4: ConfigV1_1_54 = updated_step3.try_into()?;
let updated = updated_step4.try_upgrade()?;
let updated = updated_step3.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -207,8 +205,7 @@ async fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated_step2, gateway_config) = updated_step1.upgrade()?;
let old_paths = updated_step2.storage_paths.clone();
let updated_step3: ConfigV1_1_54 = updated_step2.try_into()?;
let updated = updated_step3.try_upgrade()?;
let updated = updated_step2.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -232,8 +229,7 @@ async fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
let (updated_step1, gateway_config) = old_config.upgrade()?;
let old_paths = updated_step1.storage_paths.clone();
let updated_step2: ConfigV1_1_54 = updated_step1.try_into()?;
let updated = updated_step2.try_upgrade()?;
let updated = updated_step1.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -256,8 +252,7 @@ async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, ClientError> {
info!("It is going to get updated to the current specification.");
let old_paths = old_config.storage_paths.clone();
let updated_step1: ConfigV1_1_54 = old_config.try_into()?;
let updated = updated_step1.try_upgrade()?;
let updated = old_config.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -270,22 +265,6 @@ async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, ClientError> {
Ok(true)
}
async fn try_upgrade_v1_1_54_config(id: &str) -> Result<bool, ClientError> {
// explicitly load it as v1.1.54 (which is incompatible with the current one, i.e. +1.1.55)
let Ok(old_config) = ConfigV1_1_54::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.54 config template.");
info!("It is going to get updated to the current specification.");
let updated = old_config.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
async fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
if try_upgrade_v1_1_13_config(id).await? {
return Ok(());
@@ -299,9 +278,6 @@ async fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
if try_upgrade_v1_1_33_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_54_config(id).await? {
return Ok(());
}
Ok(())
}
+2 -2
View File
@@ -4,7 +4,7 @@
use std::error::Error;
use clap::{crate_name, crate_version, Parser};
use nym_bin_common::logging::{maybe_print_banner, setup_tracing_logger};
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_network_defaults::setup_env;
pub mod client;
@@ -20,7 +20,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if !args.no_banner {
maybe_print_banner(crate_name!(), crate_version!());
}
setup_tracing_logger();
setup_logging();
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
+1 -1
View File
@@ -318,7 +318,7 @@ impl Handler {
async fn handle_text_message(&mut self, msg: String) -> Option<WsMessage> {
debug!("Handling text message request");
trace!("Content: {msg:?}");
trace!("Content: {:?}", msg);
self.received_response_type = ReceivedResponseType::Text;
let client_request = ClientRequest::try_from_text(msg);
+2 -2
View File
@@ -68,9 +68,9 @@ impl Listener {
new_conn = tcp_listener.accept() => {
match new_conn {
Ok((mut socket, remote_addr)) => {
debug!("Received connection from {remote_addr:?}");
debug!("Received connection from {:?}", remote_addr);
if self.state.is_connected() {
warn!("Tried to open a duplicate websocket connection. The request came from {remote_addr}");
warn!("Tried to open a duplicate websocket connection. The request came from {}", remote_addr);
// if we've already got a connection, don't allow another one
// while we only ever want to accept a single connection, we don't want
// to leave clients hanging (and also allow for reconnection if it somehow
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.57"
version = "1.1.53"
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"
@@ -27,7 +27,6 @@ zeroize = { workspace = true }
nym-bin-common = { path = "../../common/bin-common", features = [
"output_format",
"clap",
"basic_tracing",
] }
nym-client-core = { path = "../../common/client-core", features = [
"fs-credentials-storage",
+10 -36
View File
@@ -7,7 +7,6 @@ use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::config::old_config_v1_1_54::ConfigV1_1_54;
use crate::config::{BaseClientConfig, Config};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
@@ -205,16 +204,15 @@ async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = updated_step3.storage_paths.clone();
let updated_step4: ConfigV1_1_33 = updated_step3.into();
let updated_step5: ConfigV1_1_54 = updated_step4.try_into()?;
let updated = updated_step4.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step5.storage_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated = updated_step5.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -236,16 +234,15 @@ async fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = updated_step2.storage_paths.clone();
let updated_step3: ConfigV1_1_33 = updated_step2.into();
let updated_step4: ConfigV1_1_54 = updated_step3.try_into()?;
let updated = updated_step3.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step4.storage_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated = updated_step4.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -264,17 +261,15 @@ async fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientErro
let old_paths = updated_step1.storage_paths.clone();
let updated_step2: ConfigV1_1_33 = updated_step1.into();
let updated_step3: ConfigV1_1_54 = updated_step2.try_into()?;
let updated = updated_step2.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step3.storage_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated = updated_step3.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -292,16 +287,15 @@ async fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = old_config.storage_paths.clone();
let updated_step1: ConfigV1_1_33 = old_config.into();
let updated_step2: ConfigV1_1_54 = updated_step1.try_into()?;
let updated = updated_step1.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step2.storage_paths.common_paths,
&updated.storage_paths.common_paths,
None,
)
.await?;
let updated = updated_step2.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -318,32 +312,15 @@ async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = old_config.storage_paths.clone();
let updated_step1: ConfigV1_1_54 = old_config.try_into()?;
let updated = old_config.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step1.storage_paths.common_paths,
&updated.storage_paths.common_paths,
None,
)
.await?;
let updated = updated_step1.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
async fn try_upgrade_v1_1_54_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.54 (which is incompatible with the current one, i.e. +1.1.55)
let Ok(old_config) = ConfigV1_1_54::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.54 config template.");
info!("It is going to get updated to the current specification.");
let updated = old_config.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -364,9 +341,6 @@ async fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
if try_upgrade_v1_1_33_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_54_config(id).await? {
return Ok(());
}
Ok(())
}
-1
View File
@@ -25,7 +25,6 @@ pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
pub mod old_config_v1_1_33;
pub mod old_config_v1_1_54;
mod persistence;
mod template;
+11 -17
View File
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{default_config_filepath, SocksClientPaths};
use crate::config::{default_config_filepath, Config, SocksClientPaths};
use crate::error::Socks5ClientError;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_33::CommonClientPathsV1_1_33;
@@ -11,8 +11,6 @@ use serde::{Deserialize, Serialize};
use std::io;
use std::path::Path;
use super::old_config_v1_1_54::ConfigV1_1_54;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct SocksClientPathsV1_1_33 {
#[serde(flatten)]
@@ -30,20 +28,6 @@ pub struct ConfigV1_1_33 {
pub logging: LoggingSettings,
}
impl TryFrom<ConfigV1_1_33> for ConfigV1_1_54 {
type Error = Socks5ClientError;
fn try_from(value: ConfigV1_1_33) -> Result<Self, Self::Error> {
Ok(ConfigV1_1_54 {
core: value.core.into(),
storage_paths: SocksClientPaths {
common_paths: value.storage_paths.common_paths.upgrade_default()?,
},
logging: value.logging,
})
}
}
impl ConfigV1_1_33 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
@@ -52,4 +36,14 @@ impl ConfigV1_1_33 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, Socks5ClientError> {
Ok(Config {
core: self.core.into(),
storage_paths: SocksClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
})
}
}
@@ -1,39 +0,0 @@
use std::{io, path::Path};
use nym_bin_common::logging::LoggingSettings;
use nym_config::read_config_from_toml_file;
use nym_socks5_client_core::config::old_config_v1_1_54::ConfigV1_1_54 as CoreConfigV1_1_54;
use serde::{Deserialize, Serialize};
use crate::config::Config;
use crate::error::Socks5ClientError;
use super::{default_config_filepath, SocksClientPaths};
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_54 {
pub core: CoreConfigV1_1_54,
pub storage_paths: SocksClientPaths,
pub logging: LoggingSettings,
}
impl ConfigV1_1_54 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, Socks5ClientError> {
Ok(Config {
core: self.core.into(),
storage_paths: self.storage_paths,
logging: self.logging,
})
}
}
+4
View File
@@ -98,6 +98,10 @@ send_anonymously = {{ core.socks5.send_anonymously }}
[core.debug]
[core.debug.traffic]
average_packet_delay = '{{ core.debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ core.debug.traffic.message_sending_average_delay }}'
[core.debug.acknowledgements]
average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}'
+2 -2
View File
@@ -4,7 +4,7 @@
use std::error::Error;
use clap::{crate_name, crate_version, Parser};
use nym_bin_common::logging::{maybe_print_banner, setup_tracing_logger};
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_network_defaults::setup_env;
mod commands;
@@ -19,7 +19,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if !args.no_banner {
maybe_print_banner(crate_name!(), crate_version!());
}
setup_tracing_logger();
setup_logging();
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
+1 -1
View File
@@ -137,7 +137,7 @@ impl AsyncFileWatcher {
log::error!("the file watcher receiver has been dropped!");
}
} else {
log::debug!("will not propagate information about {event:?}");
log::debug!("will not propagate information about {:?}", event);
}
}
Err(err) => {
@@ -108,7 +108,7 @@ impl GatewayClient {
#[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);
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -117,7 +117,7 @@ impl GatewayClient {
#[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);
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -117,7 +117,7 @@ impl GatewayClient {
#[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);
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -169,7 +169,7 @@ impl GatewayClient {
#[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);
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -169,7 +169,7 @@ impl GatewayClient {
#[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);
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
+1 -1
View File
@@ -11,7 +11,7 @@ impl std::fmt::Display for BandwidthStatusMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BandwidthStatusMessage::RemainingBandwidth(b) => {
write!(f, "remaining bandwidth: {b}")
write!(f, "remaining bandwidth: {}", b)
}
BandwidthStatusMessage::NoBandwidth => write!(f, "no bandwidth left"),
}
+1
View File
@@ -13,6 +13,7 @@ clap_complete = { workspace = true, optional = true }
clap_complete_fig = { workspace = true, optional = true }
const-str = { workspace = true }
log = { workspace = true }
pretty_env_logger = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"], optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
+23
View File
@@ -21,6 +21,29 @@ pub struct LoggingSettings {
// well, we need to implement something here at some point...
}
// I'd argue we should start transitioning from `log` to `tracing`
pub fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder();
if let Ok(s) = ::std::env::var("RUST_LOG") {
log_builder.parse_filters(&s);
} else {
// default to 'Info'
log_builder.filter(None, log::LevelFilter::Info);
}
log_builder
.filter_module("hyper", log::LevelFilter::Warn)
.filter_module("tokio_reactor", log::LevelFilter::Warn)
.filter_module("reqwest", log::LevelFilter::Warn)
.filter_module("mio", log::LevelFilter::Warn)
.filter_module("want", log::LevelFilter::Warn)
.filter_module("tungstenite", log::LevelFilter::Warn)
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
.filter_module("handlebars", log::LevelFilter::Warn)
.filter_module("sled", log::LevelFilter::Warn)
.init();
}
// don't call init so that we could attach additional layers
#[cfg(feature = "basic_tracing")]
pub fn build_tracing_logger() -> impl tracing_subscriber::layer::SubscriberExt {
+1 -4
View File
@@ -27,7 +27,6 @@ thiserror = { workspace = true }
url = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["macros"] }
time = { workspace = true }
tracing = { workspace = true }
zeroize = { workspace = true }
# internal
@@ -44,6 +43,7 @@ nym-sphinx = { path = "../nymsphinx" }
nym-statistics-common = { path = "../statistics" }
nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["persistence"] }
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" }
nym-credentials-interface = { path = "../credentials-interface" }
@@ -56,9 +56,6 @@ nym-client-core-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" }
nym-ecash-time = { path = "../ecash-time" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
### For serving prometheus metrics
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper]
workspace = true
@@ -19,7 +19,6 @@ nym-pemstore = { path = "../../pemstore", optional = true }
# those are pulling so many deps T.T
nym-sphinx-params = { path = "../../nymsphinx/params" }
nym-sphinx-addressing = { path = "../../nymsphinx/addressing" }
nym-statistics-common = { path = "../../statistics" }
[features]
+3 -70
View File
@@ -5,7 +5,6 @@ use nym_config::defaults::NymNetworkDetails;
use nym_config::serde_helpers::{de_maybe_stringified, ser_maybe_stringified};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_statistics_common::types::SessionType;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -23,7 +22,7 @@ const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(15);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
@@ -376,12 +375,14 @@ pub struct Traffic {
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
@@ -413,15 +414,6 @@ pub struct Traffic {
pub use_legacy_sphinx_format: bool,
pub packet_type: PacketType,
/// Indicates whether to mix hops or not. If mix hops are enabled, traffic
/// will be routed as usual, to the entry gateway, through three mix nodes, egressing
/// through the exit gateway. If mix hops are disabled, traffic will be routed directly
/// from the entry gateway to the exit gateway, bypassing the mix nodes.
///
/// This overrides the `use_legacy_sphinx_format` setting as reduced mix hops
/// requires use of the updated SURB packet format.
pub disable_mix_hops: bool,
}
impl Traffic {
@@ -452,7 +444,6 @@ impl Default for Traffic {
// we should use the legacy format until sufficient number of nodes understand the
// improved encoding
use_legacy_sphinx_format: true,
disable_mix_hops: false,
}
}
}
@@ -720,9 +711,6 @@ pub struct DebugConfig {
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMe,
/// Defines all configuration options related to the remember me flag.
pub remember_me: RememberMe,
}
impl DebugConfig {
@@ -746,7 +734,6 @@ impl Default for DebugConfig {
reply_surbs: Default::default(),
stats_reporting: Default::default(),
forget_me: Default::default(),
remember_me: Default::default(),
}
}
}
@@ -812,57 +799,3 @@ impl ForgetMe {
}
}
}
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
pub struct RememberMe {
/// Signal that this client should be accounted for in the stats
stats: bool,
/// Type of the session to remember, if it should be remembered
session_type: SessionType,
}
impl RememberMe {
pub fn new_vpn() -> Self {
Self {
stats: true,
session_type: SessionType::Vpn,
}
}
pub fn new_mixnet() -> Self {
Self {
stats: true,
session_type: SessionType::Mixnet,
}
}
pub fn new_native() -> Self {
Self {
stats: true,
session_type: SessionType::Native,
}
}
pub fn new(stats: bool, session_type: SessionType) -> Self {
Self {
stats,
session_type,
}
}
pub fn new_none() -> Self {
Self {
stats: false,
session_type: SessionType::Unknown,
}
}
pub fn session_type(&self) -> SessionType {
self.session_type
}
pub fn stats(&self) -> bool {
self.stats
}
}
@@ -6,7 +6,6 @@ pub mod v2;
pub mod v3;
pub mod v4;
pub mod v5;
pub mod v6;
// aliases for backwards compatibility
pub use v1 as old_config_v1_1_13;
@@ -14,4 +13,3 @@ pub use v2 as old_config_v1_1_20;
pub use v3 as old_config_v1_1_20_2;
pub use v4 as old_config_v1_1_30;
pub use v5 as old_config_v1_1_33;
pub use v6 as old_config_v1_1_54;
+14 -12
View File
@@ -1,14 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, ReplySurbs,
Topology, Traffic,
};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
use super::v6::*;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
@@ -85,18 +87,18 @@ pub struct ConfigV5 {
pub debug: DebugConfigV5,
}
impl From<ConfigV5> for ConfigV6 {
impl From<ConfigV5> for Config {
fn from(value: ConfigV5) -> Self {
ConfigV6 {
client: ClientV6 {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfigV6 {
traffic: TrafficV6 {
debug: DebugConfig {
traffic: Traffic {
average_packet_delay: value.debug.traffic.average_packet_delay,
message_sending_average_delay: value
.debug
@@ -111,7 +113,7 @@ impl From<ConfigV5> for ConfigV6 {
packet_type: value.debug.traffic.packet_type,
..Default::default()
},
cover_traffic: CoverTrafficV6 {
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
@@ -125,18 +127,18 @@ impl From<ConfigV5> for ConfigV6 {
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnectionV6 {
gateway_connection: GatewayConnection {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: AcknowledgementsV6 {
acknowledgements: Acknowledgements {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: TopologyV6 {
topology: Topology {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
@@ -146,7 +148,7 @@ impl From<ConfigV5> for ConfigV6 {
.max_startup_gateway_waiting_period,
..Default::default()
},
reply_surbs: ReplySurbsV6 {
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
@@ -1,623 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, ForgetMe, GatewayConnection,
RememberMe, ReplySurbs, StatsReporting, Topology, Traffic,
};
use nym_config::serde_helpers::{de_maybe_stringified, ser_maybe_stringified};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_statistics_common::types::SessionType;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(15);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
// the same values as our current (10.06.24) blacklist
const DEFAULT_MIN_MIXNODE_PERFORMANCE: u8 = 50;
const DEFAULT_MIN_GATEWAY_PERFORMANCE: u8 = 50;
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
const DEFAULT_MINIMUM_REPLY_SURB_THRESHOLD_BUFFER: usize = 0;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 50;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// stats reporting related
/// Time interval between reporting statistics to the given provider if it exists
const STATS_REPORT_INTERVAL_SECS: Duration = Duration::from_secs(300);
// aliases for backwards compatibility
pub type ConfigV1_1_54 = ConfigV6;
pub type ClientV1_1_54 = ClientV6;
pub type DebugConfigV1_1_54 = DebugConfigV6;
pub type TrafficV1_1_54 = TrafficV6;
pub type CoverTrafficV1_1_54 = CoverTrafficV6;
pub type GatewayConnectionV1_1_54 = GatewayConnectionV6;
pub type AcknowledgementsV1_1_54 = AcknowledgementsV6;
pub type TopologyV1_1_54 = TopologyV6;
pub type ReplySurbsV1_1_54 = ReplySurbsV6;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV6 {
pub client: ClientV6,
#[serde(default)]
pub debug: DebugConfigV6,
}
impl From<ConfigV6> for Config {
fn from(value: ConfigV6) -> Self {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfig {
traffic: Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: value
.debug
.traffic
.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.debug
.traffic
.disable_main_poisson_packet_distribution,
primary_packet_size: value.debug.traffic.primary_packet_size,
secondary_packet_size: value.debug.traffic.secondary_packet_size,
packet_type: value.debug.traffic.packet_type,
deterministic_route_selection: value
.debug
.traffic
.deterministic_route_selection,
maximum_number_of_retransmissions: value
.debug
.traffic
.maximum_number_of_retransmissions,
use_legacy_sphinx_format: value.debug.traffic.use_legacy_sphinx_format,
disable_mix_hops: value.debug.traffic.disable_mix_hops,
},
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value
.debug
.cover_traffic
.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value
.debug
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnection {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: Topology {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
max_startup_gateway_waiting_period: value
.debug
.topology
.max_startup_gateway_waiting_period,
minimum_mixnode_performance: value.debug.topology.minimum_mixnode_performance,
minimum_gateway_performance: value.debug.topology.minimum_gateway_performance,
use_extended_topology: value.debug.topology.use_extended_topology,
ignore_egress_epoch_role: value.debug.topology.ignore_egress_epoch_role,
ignore_ingress_epoch_role: value.debug.topology.ignore_ingress_epoch_role,
},
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value
.debug
.reply_surbs
.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.debug.reply_surbs.maximum_reply_surb_age,
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
minimum_reply_surb_threshold_buffer: value
.debug
.reply_surbs
.minimum_reply_surb_threshold_buffer,
fresh_sender_tags: value.debug.reply_surbs.fresh_sender_tags,
},
stats_reporting: StatsReporting {
enabled: value.debug.stats_reporting.enabled,
provider_address: value.debug.stats_reporting.provider_address,
reporting_interval: value.debug.stats_reporting.reporting_interval,
},
forget_me: ForgetMe {
client: value.debug.forget_me.client,
stats: value.debug.forget_me.stats,
},
remember_me: RememberMe {
stats: value.debug.remember_me.stats,
session_type: value.debug.remember_me.session_type.into(),
},
},
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct ClientV6 {
/// Version of the client for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TrafficV6 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Specify whether route selection should be determined by the packet header.
pub deterministic_route_selection: bool,
/// Specify how many times particular packet can be retransmitted
/// None - no limit
pub maximum_number_of_retransmissions: Option<u32>,
/// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity.
/// Do not set it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
/// Specify whether any constructed sphinx packets should use the legacy format,
/// where the payload keys are explicitly attached rather than using the seeds
/// this affects any forward packets, acks and reply surbs
/// this flag should remain disabled until sufficient number of nodes on the network has upgraded
/// and support updated format.
/// in the case of reply surbs, the recipient must also understand the new encoding
pub use_legacy_sphinx_format: bool,
pub packet_type: PacketType,
/// Indicates whether to mix hops or not. If mix hops are enabled, traffic
/// will be routed as usual, to the entry gateway, through three mix nodes, egressing
/// through the exit gateway. If mix hops are disabled, traffic will be routed directly
/// from the entry gateway to the exit gateway, bypassing the mix nodes.
pub disable_mix_hops: bool,
}
impl Default for TrafficV6 {
fn default() -> Self {
TrafficV6 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
deterministic_route_selection: false,
maximum_number_of_retransmissions: None,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
// we should use the legacy format until sufficient number of nodes understand the
// improved encoding
use_legacy_sphinx_format: true,
disable_mix_hops: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV6 {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
/// Only applicable if `secondary_packet_size` is enabled.
pub cover_traffic_primary_size_ratio: f64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTrafficV6 {
fn default() -> Self {
CoverTrafficV6 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV6 {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnectionV6 {
fn default() -> Self {
GatewayConnectionV6 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV6 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for AcknowledgementsV6 {
fn default() -> Self {
AcknowledgementsV6 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV6 {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies a minimum performance of a mixnode that is used on route construction.
/// This setting is only applicable when `NymApi` topology is used.
pub minimum_mixnode_performance: u8,
/// Specifies a minimum performance of a gateway that is used on route construction.
/// This setting is only applicable when `NymApi` topology is used.
pub minimum_gateway_performance: u8,
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool,
/// Specifies whether this client should ignore the current epoch role of the ingress node
/// when attempting to establish new connection
pub ignore_ingress_epoch_role: bool,
}
impl Default for TopologyV6 {
fn default() -> Self {
TopologyV6 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
use_extended_topology: false,
ignore_egress_epoch_role: true,
ignore_ingress_epoch_role: true,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV6 {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
pub maximum_reply_surb_storage_threshold: usize,
/// Defines the soft threshold ontop of the minimum reply surb storage threshold for when the client
/// should proactively request additional reply surbs.
pub minimum_reply_surb_threshold_buffer: usize,
/// Defines the minimum number of reply surbs the client would request.
pub minimum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs the client would request.
pub maximum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
pub maximum_allowed_reply_surb_request_size: u32,
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
/// for more even though in theory they wouldn't need to.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
/// Defines maximum amount of time the client is going to wait for reply surbs before
/// deciding it's never going to get them and would drop all pending messages
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
/// Defines maximum amount of time given reply surb is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
/// Defines maximum amount of time given reply key is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
/// the default value is used.
pub surb_mix_hops: Option<u8>,
/// Specifies if we should reset all the sender tags on startup
pub fresh_sender_tags: bool,
}
impl Default for ReplySurbsV6 {
fn default() -> Self {
ReplySurbsV6 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_threshold_buffer: DEFAULT_MINIMUM_REPLY_SURB_THRESHOLD_BUFFER,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
surb_mix_hops: None,
fresh_sender_tags: false,
}
}
}
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV6 {
/// Defines all configuration options related to traffic streams.
pub traffic: TrafficV6,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficV6,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionV6,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsV6,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyV6,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV6,
/// Defines all configuration options related to stats reporting.
pub stats_reporting: StatsReportingV6,
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMeV6,
/// Defines all configuration options related to the remember me flag.
pub remember_me: RememberMeV6,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StatsReportingV6 {
/// Is stats reporting enabled
pub enabled: bool,
/// Address of the stats collector. If this is none, no reporting will happen, regardless of `enabled`
#[serde(
serialize_with = "ser_maybe_stringified",
deserialize_with = "de_maybe_stringified"
)]
pub provider_address: Option<Recipient>,
/// With what frequence will statistics be sent
#[serde(with = "humantime_serde")]
pub reporting_interval: Duration,
}
impl Default for StatsReportingV6 {
fn default() -> Self {
StatsReportingV6 {
enabled: true,
provider_address: None,
reporting_interval: STATS_REPORT_INTERVAL_SECS,
}
}
}
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
pub struct ForgetMeV6 {
client: bool,
stats: bool,
}
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
pub struct RememberMeV6 {
/// Signal that this client should be accounted for in the stats
stats: bool,
/// Type of the session to remember, if it should be remembered
session_type: SessionTypeV6,
}
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize, Default, Debug)]
pub enum SessionTypeV6 {
Vpn,
Mixnet,
Wasm,
Native,
Socks5,
#[default]
Unknown,
}
impl From<SessionTypeV6> for SessionType {
fn from(value: SessionTypeV6) -> Self {
match value {
SessionTypeV6::Vpn => Self::Vpn,
SessionTypeV6::Mixnet => Self::Mixnet,
SessionTypeV6::Wasm => Self::Wasm,
SessionTypeV6::Native => Self::Native,
SessionTypeV6::Socks5 => Self::Socks5,
SessionTypeV6::Unknown => Self::Unknown,
}
}
}
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::BadGateway;
use std::{io, path::PathBuf};
use std::io;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -18,6 +19,7 @@ pub enum StorageError {
#[error("failed to perform sqlx migration: {source}")]
MigrationError {
#[source]
#[from]
source: sqlx::migrate::MigrateError,
},
@@ -30,6 +32,7 @@ pub enum StorageError {
#[error("failed to run the SQL query: {source}")]
QueryError {
#[source]
#[from]
source: sqlx::error::Error,
},
@@ -87,7 +87,7 @@ impl StorageManager {
sqlx::query!("SELECT EXISTS (SELECT 1 FROM registered_gateway WHERE gateway_id_bs58 = ?) AS 'exists'", gateway_id)
.fetch_one(&self.connection_pool)
.await
.map(|result| result.exists == 1)
.map(|result| result.exists == Some(1))
}
pub(crate) async fn maybe_get_registered_gateway(
@@ -36,7 +36,7 @@ use crate::{config, spawn_future};
use futures::channel::mpsc;
use log::*;
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_config_types::{ForgetMe, RememberMe};
use nym_client_core_config_types::ForgetMe;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::{ed25519, x25519};
@@ -238,12 +238,6 @@ where
self
}
#[must_use]
pub fn with_remember_me(mut self, remember_me: &RememberMe) -> Self {
self.config.debug.remember_me = *remember_me;
self
}
#[must_use]
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
@@ -456,7 +450,7 @@ where
log::error!("Could not authenticate and start up the gateway connection - {err}");
ClientCoreError::GatewayClientError {
gateway_id: details.gateway_id.to_base58_string(),
source: Box::new(err),
source: err,
}
};
@@ -936,7 +930,6 @@ where
task_handle: shutdown,
client_request_sender,
forget_me: self.config.debug.forget_me,
remember_me: self.config.debug.remember_me,
})
}
}
@@ -951,5 +944,4 @@ pub struct BaseClient {
pub client_request_sender: ClientRequestSender,
pub task_handle: TaskHandle,
pub forget_me: ForgetMe,
pub remember_me: RememberMe,
}
@@ -1,18 +1,20 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
client::replies::reply_storage::{fs_backend, CombinedReplyStorage, ReplyStorageBackend},
config,
config::Config,
error::ClientCoreError,
use crate::client::replies::reply_storage::{
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
};
use crate::config;
use crate::config::Config;
use crate::error::ClientCoreError;
use log::{error, info, trace};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_gateways_storage::OnDiskGatewaysDetails;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_validator_client::{nyxd, QueryHttpRpcNyxdClient};
use std::{io, path::Path};
use nym_validator_client::nyxd;
use nym_validator_client::QueryHttpRpcNyxdClient;
use std::path::Path;
use std::{fs, io};
use time::OffsetDateTime;
use url::Url;
@@ -20,11 +22,11 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
db_path: P,
surb_config: &config::ReplySurbs,
) -> Result<fs_backend::Backend, ClientCoreError> {
info!("Creating fresh surb database");
info!("creating fresh surb database");
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
Ok(backend) => backend,
Err(err) => {
error!("setup_fresh_backend: Failed to setup persistent storage backend for our reply needs: {err}");
error!("failed to setup persistent storage backend for our reply needs: {err}");
return Err(ClientCoreError::SurbStorageError {
source: Box::new(err),
});
@@ -38,15 +40,14 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
surb_config.minimum_reply_surb_storage_threshold,
surb_config.maximum_reply_surb_storage_threshold,
);
match storage_backend.init_fresh(&mem_store).await {
Ok(()) => Ok(storage_backend),
Err(err) => {
storage_backend.shutdown().await;
Err(ClientCoreError::SurbStorageError {
source: Box::new(err),
})
}
}
storage_backend
.init_fresh(&mem_store)
.await
.map_err(|err| ClientCoreError::SurbStorageError {
source: Box::new(err),
})?;
Ok(storage_backend)
}
// fn setup_inactive_backend(surb_config: &config::ReplySurbs) -> fs_backend::Backend {
@@ -57,11 +58,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
// )
// }
async fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
let db_path = db_path.as_ref();
debug_assert!(db_path.exists());
let now = OffsetDateTime::now_utc().unix_timestamp();
let suffix = format!("_{now}.corrupted");
let new_extension =
@@ -70,15 +72,11 @@ async fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()
} else {
suffix
};
let renamed = db_path.with_extension(new_extension);
tokio::fs::rename(db_path, &renamed).await.inspect_err(|_| {
error!(
"Failed to rename corrupt database file: {} to {}",
db_path.display(),
renamed.display()
);
})
let mut renamed = db_path.to_owned();
renamed.set_extension(new_extension);
fs::rename(db_path, renamed)
}
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
@@ -89,12 +87,13 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
// the existing one
let db_path = db_path.as_ref();
if db_path.exists() {
info!("Loading existing surb database");
info!("loading existing surb database");
match fs_backend::Backend::try_load(db_path, surb_config.fresh_sender_tags).await {
Ok(backend) => Ok(backend),
Err(err) => {
error!("setup_fs_reply_surb_backend: Failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
archive_corrupted_database(db_path).await?;
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
archive_corrupted_database(db_path)?;
setup_fresh_backend(db_path, surb_config).await
}
}
@@ -146,7 +146,7 @@ impl MixTrafficController {
Some(client_request) => {
match self.gateway_transceiver.send_client_request(client_request).await {
Ok(_) => (),
Err(e) => error!("Failed to send client request: {e}"),
Err(e) => error!("Failed to send client request: {}", e),
};
},
None => {
@@ -65,7 +65,7 @@ impl AcknowledgementListener {
return;
}
trace!("Received {frag_id} from the mix network");
trace!("Received {} from the mix network", frag_id);
self.stats_tx
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into());
if let Err(err) = self
@@ -126,7 +126,7 @@ impl ActionController {
fn handle_insert(&mut self, pending_acks: Vec<PendingAcknowledgement>) {
for pending_ack in pending_acks {
let frag_id = pending_ack.message_chunk.fragment_identifier();
trace!("{frag_id} is inserted");
trace!("{} is inserted", frag_id);
if self
.pending_acks_data
@@ -161,16 +161,22 @@ impl ActionController {
let new_queue_key = self.pending_acks_timers.insert(frag_id, timeout);
*queue_key = Some(new_queue_key)
} else {
debug!("Tried to START TIMER on pending ack that is already gone! - {frag_id}");
debug!(
"Tried to START TIMER on pending ack that is already gone! - {}",
frag_id
);
}
}
fn handle_remove(&mut self, frag_id: FragmentIdentifier) {
trace!("{frag_id} is getting removed");
trace!("{} is getting removed", frag_id);
match self.pending_acks_data.remove(&frag_id) {
None => {
debug!("Tried to REMOVE pending ack that is already gone! - {frag_id}");
debug!(
"Tried to REMOVE pending ack that is already gone! - {}",
frag_id
);
}
Some((_, queue_key)) => {
if let Some(queue_key) = queue_key {
@@ -182,7 +188,10 @@ impl ActionController {
} else {
// I'm not 100% sure if having a `None` key is even possible here
// (REMOVE would have to be called before START TIMER),
debug!("Tried to REMOVE pending ack without TIMER active - {frag_id}");
debug!(
"Tried to REMOVE pending ack without TIMER active - {}",
frag_id
);
}
}
}
@@ -191,7 +200,7 @@ impl ActionController {
// initiated basically as a first step of retransmission. At first data has its delay updated
// (as new sphinx packet was created with new expected delivery time)
fn handle_update_pending_ack(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
trace!("{frag_id} is updating its delay");
trace!("{} is updating its delay", frag_id);
// TODO: is it possible to solve this without either locking or temporarily removing the value?
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
// this Action is triggered by `RetransmissionRequestListener` (for 'normal' packets)
@@ -204,7 +213,10 @@ impl ActionController {
self.pending_acks_data
.insert(frag_id, (Arc::new(inner_data), queue_key));
} else {
debug!("Tried to UPDATE TIMER on pending ack that is already gone! - {frag_id}");
debug!(
"Tried to UPDATE TIMER on pending ack that is already gone! - {}",
frag_id
);
}
}
@@ -9,10 +9,11 @@ use crate::client::real_messages_control::{AckActionSender, Action};
use crate::client::replies::reply_controller::MaxRetransmissions;
use crate::client::replies::reply_storage::{ReceivedReplySurbsMap, SentReplyKeys, UsedSenderTags};
use crate::client::topology_control::{TopologyAccessor, TopologyReadPermit};
use log::{debug, error, info, trace, warn};
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessage, ReplyMessage};
use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation;
use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey};
use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier};
use nym_sphinx::message::NymMessage;
use nym_sphinx::params::{PacketSize, PacketType};
@@ -26,7 +27,6 @@ use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use thiserror::Error;
use tracing::{debug, error, info, trace, warn};
// TODO: move that error elsewhere since it seems to be contaminating different files
#[derive(Debug, Error)]
@@ -44,10 +44,7 @@ pub enum PreparationError {
}
impl PreparationError {
fn return_surbs(
self,
returned_surbs: Vec<ReplySurbWithKeyRotation>,
) -> SurbWrappedPreparationError {
fn return_surbs(self, returned_surbs: Vec<ReplySurb>) -> SurbWrappedPreparationError {
SurbWrappedPreparationError {
source: self,
returned_surbs: Some(returned_surbs),
@@ -61,7 +58,7 @@ pub struct SurbWrappedPreparationError {
#[source]
source: PreparationError,
returned_surbs: Option<Vec<ReplySurbWithKeyRotation>>,
returned_surbs: Option<Vec<ReplySurb>>,
}
impl<T> From<T> for SurbWrappedPreparationError
@@ -101,15 +98,6 @@ pub(crate) struct Config {
/// Specify whether route selection should be determined by the packet header.
deterministic_route_selection: bool,
/// Indicates whether to mix hops or not. If mix hops are enabled, traffic
/// will be routed as usual, to the entry gateway, through three mix nodes, egressing
/// through the exit gateway. If mix hops are disabled, traffic will be routed directly
/// from the entry gateway to the exit gateway, bypassing the mix nodes.
///
/// This overrides the `use_legacy_sphinx_format` setting as reduced mix hops
/// requires use of the updated SURB packet format.
disable_mix_hops: bool,
/// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: Duration,
@@ -145,7 +133,6 @@ impl Config {
primary_packet_size: PacketSize::default(),
secondary_packet_size: None,
use_legacy_sphinx_format: use_legacy_reply_surb_format,
disable_mix_hops: false,
}
}
@@ -160,16 +147,6 @@ impl Config {
self.secondary_packet_size = packet_size;
self
}
/// Configure whether messages senders using this config should use mix hops or not when sending messages.
///
/// This overrides the `use_legacy_sphinx_format` setting as disabled mix hops
/// requires use of the updated SURB packet format.
pub fn disable_mix_hops(mut self, disable_mix_hops: bool) -> Self {
self.disable_mix_hops = disable_mix_hops;
self.use_legacy_sphinx_format = false;
self
}
}
#[derive(Clone)]
@@ -216,7 +193,6 @@ where
config.average_packet_delay,
config.average_ack_delay,
config.use_legacy_sphinx_format,
config.disable_mix_hops,
);
MessageHandler {
config,
@@ -278,10 +254,10 @@ where
}
}
async fn generate_reply_surbs(
async fn generate_reply_surbs_with_keys(
&mut self,
amount: usize,
) -> Result<Vec<ReplySurbWithKeyRotation>, PreparationError> {
) -> Result<(Vec<ReplySurb>, Vec<SurbEncryptionKey>), PreparationError> {
let topology_permit = self.topology_access.get_read_permit().await;
let topology = self.get_topology(&topology_permit)?;
@@ -291,19 +267,24 @@ where
topology,
)?;
Ok(reply_surbs)
let reply_keys = reply_surbs
.iter()
.map(|s| *s.encryption_key())
.collect::<Vec<_>>();
Ok((reply_surbs, reply_keys))
}
pub(crate) async fn try_send_single_surb_message(
&mut self,
target: AnonymousSenderTag,
message: ReplyMessage,
reply_surb: ReplySurbWithKeyRotation,
reply_surb: ReplySurb,
is_extra_surb_request: bool,
) -> Result<(), SurbWrappedPreparationError> {
let msg = NymMessage::new_reply(message);
let packet_size = self.optimal_packet_size(&msg);
trace!("Using {packet_size} packets for {msg}");
debug!("Using {packet_size} packets for {msg}");
let mut fragment = self
.message_preparer
@@ -352,7 +333,7 @@ where
pub(crate) async fn try_request_additional_reply_surbs(
&mut self,
from: AnonymousSenderTag,
reply_surb: ReplySurbWithKeyRotation,
reply_surb: ReplySurb,
amount: u32,
) -> Result<(), SurbWrappedPreparationError> {
debug!("requesting {amount} reply SURBs from {from}");
@@ -367,7 +348,7 @@ where
pub(crate) fn split_reply_message(&mut self, message: Vec<u8>) -> Vec<Fragment> {
let msg = NymMessage::new_reply(ReplyMessage::new_data_message(message));
let packet_size = self.optimal_packet_size(&msg);
trace!("Using {packet_size} packets for {msg}");
debug!("Using {packet_size} packets for {msg}");
self.message_preparer
.pad_and_split_message(msg, packet_size)
@@ -392,7 +373,7 @@ where
&mut self,
target: AnonymousSenderTag,
fragments: Vec<FragmentWithMaxRetransmissions>,
reply_surbs: Vec<ReplySurbWithKeyRotation>,
reply_surbs: Vec<ReplySurb>,
lane: TransmissionLane,
) -> Result<(), SurbWrappedPreparationError> {
// TODO: technically this is performing an unnecessary cloning, but in the grand scheme of things
@@ -409,7 +390,7 @@ where
&mut self,
target: AnonymousSenderTag,
fragments: Vec<(TransmissionLane, FragmentWithMaxRetransmissions)>,
reply_surbs: Vec<ReplySurbWithKeyRotation>,
reply_surbs: Vec<ReplySurb>,
) -> Result<(), SurbWrappedPreparationError> {
let prepared_fragments = self
.prepare_reply_chunks_for_sending(
@@ -500,7 +481,7 @@ where
} else {
self.optimal_packet_size(&message)
};
trace!("Using {packet_size} packets for {message}");
debug!("Using {packet_size} packets for {message}");
let fragments = self
.message_preparer
.pad_and_split_message(message, packet_size);
@@ -546,12 +527,8 @@ where
) -> Result<(), PreparationError> {
debug!("Sending additional reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient);
let reply_surbs = self.generate_reply_surbs(amount as usize).await?;
let reply_keys = reply_surbs
.iter()
.map(|s| *s.encryption_key())
.collect::<Vec<_>>();
let (reply_surbs, reply_keys) =
self.generate_reply_surbs_with_keys(amount as usize).await?;
let message = NymMessage::new_repliable(RepliableMessage::new_additional_surbs(
self.config.use_legacy_sphinx_format,
@@ -588,12 +565,9 @@ where
) -> Result<(), SurbWrappedPreparationError> {
debug!("Sending message with reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient);
let reply_surbs = self.generate_reply_surbs(num_reply_surbs as usize).await?;
let reply_keys = reply_surbs
.iter()
.map(|s| *s.encryption_key())
.collect::<Vec<_>>();
let (reply_surbs, reply_keys) = self
.generate_reply_surbs_with_keys(num_reply_surbs as usize)
.await?;
let message = NymMessage::new_repliable(RepliableMessage::new_data(
self.config.use_legacy_sphinx_format,
@@ -641,7 +615,7 @@ where
pub(crate) async fn prepare_reply_chunks_for_sending(
&mut self,
fragments: Vec<Fragment>,
reply_surbs: Vec<ReplySurbWithKeyRotation>,
reply_surbs: Vec<ReplySurb>,
) -> Result<Vec<PreparedFragment>, SurbWrappedPreparationError> {
debug_assert_eq!(
fragments.len(),
@@ -677,7 +651,7 @@ where
pub(crate) async fn try_prepare_single_reply_chunk_for_sending(
&mut self,
reply_surb: ReplySurbWithKeyRotation,
reply_surb: ReplySurb,
chunk: Fragment,
) -> Result<PreparedFragment, SurbWrappedPreparationError> {
let topology_permit = self.topology_access.get_read_permit().await;
@@ -103,7 +103,6 @@ impl<'a> From<&'a Config> for message_handler::Config {
)
.with_custom_primary_packet_size(cfg.traffic.primary_packet_size)
.with_custom_secondary_packet_size(cfg.traffic.secondary_packet_size)
.disable_mix_hops(cfg.traffic.disable_mix_hops)
}
}
@@ -202,7 +202,7 @@ where
// well technically the message was not sent just yet, but now it's up to internal
// queues and client load rather than the required delay. So realistically we can treat
// whatever is about to happen as negligible additional delay.
trace!("{frag_id} is about to get sent to the mixnet");
trace!("{} is about to get sent to the mixnet", frag_id);
if let Err(err) = self.sent_notifier.unbounded_send(frag_id) {
error!("Failed to notify about sent message: {err}");
}
@@ -164,11 +164,11 @@ impl SendingDelayController {
self.current_multiplier()
);
if self.current_multiplier() > 0 {
log::debug!("{status_str}");
log::debug!("{}", status_str);
} else if self.current_multiplier() > 1 {
log::info!("{status_str}");
log::info!("{}", status_str);
} else if self.current_multiplier() > 2 {
log::warn!("{status_str}");
log::warn!("{}", status_str);
}
self.time_when_logged_about_elevated_multiplier = now;
}
@@ -221,7 +221,10 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
let stored_messages = std::mem::take(&mut guard.messages);
if !stored_messages.is_empty() {
if let Err(err) = sender.unbounded_send(stored_messages) {
error!("The sender channel we just received is already invalidated - {err:?}");
error!(
"The sender channel we just received is already invalidated - {:?}",
err
);
// put the values back to the buffer
// the returned error has two fields: err: SendError and val: T,
// where val is the value that was failed to get sent;
@@ -11,7 +11,7 @@ use futures::StreamExt;
use log::{debug, error, info, trace, warn};
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation;
use nym_sphinx::anonymous_replies::ReplySurb;
use nym_sphinx::chunking::fragment::FragmentIdentifier;
use nym_task::connections::{ConnectionId, TransmissionLane};
use nym_task::TaskClient;
@@ -217,14 +217,14 @@ where
.surbs_storage_ref()
.contains_surbs_for(&recipient_tag)
{
warn!("received reply request for {recipient_tag:?} but we don't have any surbs stored for that recipient!");
warn!("received reply request for {:?} but we don't have any surbs stored for that recipient!", recipient_tag);
return;
}
trace!("handling reply to {recipient_tag:?}");
trace!("handling reply to {:?}", recipient_tag);
let mut fragments = self.message_handler.split_reply_message(data);
let total_size = fragments.len();
trace!("This reply requires {total_size:?} SURBs");
trace!("This reply requires {:?} SURBs", total_size);
let available_surbs = self
.full_reply_storage
@@ -327,7 +327,10 @@ where
.await
{
let err = err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
warn!("failed to request additional surbs from {target:?} - {err}");
warn!(
"failed to request additional surbs from {:?} - {err}",
target
);
return Err(err);
} else {
self.full_reply_storage
@@ -406,7 +409,10 @@ where
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
self.re_insert_pending_retransmission(&target, to_take);
warn!("failed to clear pending retransmission queue for {target:?} - {err}");
warn!(
"failed to clear pending retransmission queue for {:?} - {err}",
target
);
return;
}
};
@@ -483,7 +489,7 @@ where
let err =
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
self.re_insert_pending_replies(&target, to_send);
warn!("failed to clear pending queue for {target:?} - {err}");
warn!("failed to clear pending queue for {:?} - {err}", target);
}
} else {
trace!("the pending queue is empty");
@@ -493,7 +499,7 @@ where
async fn handle_received_surbs(
&mut self,
from: AnonymousSenderTag,
reply_surbs: Vec<ReplySurbWithKeyRotation>,
reply_surbs: Vec<ReplySurb>,
from_surb_request: bool,
) {
trace!("handling received surbs");
@@ -810,7 +816,7 @@ where
if diff > max_drop_wait {
to_remove.push(*pending_reply_target)
} else {
debug!("We haven't received any surbs in {diff:?} from {pending_reply_target}. Going to explicitly ask for more");
debug!("We haven't received any surbs in {:?} from {pending_reply_target}. Going to explicitly ask for more", diff);
to_request.push(*pending_reply_target);
}
}
@@ -6,7 +6,7 @@ use futures::channel::{mpsc, oneshot};
use log::error;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation;
use nym_sphinx::anonymous_replies::ReplySurb;
use nym_task::connections::{ConnectionId, TransmissionLane};
use std::sync::Weak;
@@ -81,7 +81,7 @@ impl ReplyControllerSender {
pub(crate) fn send_additional_surbs(
&self,
sender_tag: AnonymousSenderTag,
reply_surbs: Vec<ReplySurbWithKeyRotation>,
reply_surbs: Vec<ReplySurb>,
from_surb_request: bool,
) -> Result<(), ReplyControllerSenderError> {
self.0
@@ -167,7 +167,7 @@ pub enum ReplyControllerMessage {
AdditionalSurbs {
sender_tag: AnonymousSenderTag,
reply_surbs: Vec<ReplySurbWithKeyRotation>,
reply_surbs: Vec<ReplySurb>,
from_surb_request: bool,
},
@@ -93,7 +93,7 @@ impl StatisticsControl {
None,
);
if let Err(err) = self.report_tx.send(report_message).await {
log::error!("Failed to report client stats: {err:?}");
log::error!("Failed to report client stats: {:?}", err);
} else {
self.stats.reset();
}
@@ -157,12 +157,6 @@ impl TopologyRefresher {
let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
// We already have an initial topology, so no need to refresh it immediately.
// My understanding is that js setInterval does not fire immediately, so it's not
// needed there.
#[cfg(not(target_arch = "wasm32"))]
interval.next().await;
while !self.task_client.is_shutdown() {
tokio::select! {
_ = interval.next() => {
@@ -4,7 +4,7 @@
use async_trait::async_trait;
use log::{debug, error, warn};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::{NymTopology, NymTopologyMetadata};
use nym_topology::NymTopology;
use nym_validator_client::UserAgent;
use rand::prelude::SliceRandom;
use rand::thread_rng;
@@ -70,10 +70,6 @@ impl NymApiTopologyProvider {
}
}
pub fn disable_bincode(&mut self) {
self.validator_client.use_bincode = false;
}
fn use_next_nym_api(&mut self) {
if self.nym_api_urls.len() == 1 {
warn!("There's only a single nym API available - it won't be possible to use a different one");
@@ -86,62 +82,47 @@ impl NymApiTopologyProvider {
}
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
let rewarded_set_fut = self.validator_client.get_current_rewarded_set();
let rewarded_set = self
.validator_client
.get_current_rewarded_set()
.await
.inspect_err(|err| error!("failed to get current rewarded set: {err}"))
.ok()?;
let topology = if self.config.use_extended_topology {
let all_nodes_fut = self.validator_client.get_all_basic_nodes_with_metadata();
let mut topology = NymTopology::new_empty(rewarded_set);
// Join rewarded_set_fut and all_nodes_fut concurrently
let (rewarded_set, all_nodes_res) = futures::try_join!(rewarded_set_fut, all_nodes_fut)
if self.config.use_extended_topology {
let all_nodes = self
.validator_client
.get_all_basic_nodes()
.await
.inspect_err(|err| error!("failed to get network nodes: {err}"))
.ok()?;
let metadata = all_nodes_res.metadata;
let all_nodes = all_nodes_res.nodes;
debug!(
"there are {} nodes on the network (before filtering)",
all_nodes.len()
);
let nodes_filtered = all_nodes
.into_iter()
.filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance())
.collect::<Vec<_>>();
NymTopology::new(
NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id),
rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes_filtered)
topology.add_additional_nodes(all_nodes.iter().filter(|n| {
n.performance.round_to_integer() >= self.config.min_node_performance()
}));
} else {
// if we're not using extended topology, we're only getting active set mixnodes and gateways
let mixnodes_fut = self
let mixnodes = self
.validator_client
.get_all_basic_active_mixing_assigned_nodes_with_metadata();
.get_all_basic_active_mixing_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network mixnodes: {err}"))
.ok()?;
// TODO: we really should be getting ACTIVE gateways only
let gateways_fut = self
let gateways = self
.validator_client
.get_all_basic_entry_assigned_nodes_v2();
let (rewarded_set, mixnodes_res, gateways_res) =
futures::try_join!(rewarded_set_fut, mixnodes_fut, gateways_fut)
.inspect_err(|err| {
error!("failed to get network nodes: {err}");
})
.ok()?;
let metadata = mixnodes_res.metadata;
let mixnodes = mixnodes_res.nodes;
if gateways_res.metadata != metadata {
warn!("inconsistent nodes metadata between mixnodes and gateways calls! {metadata:?} and {:?}", gateways_res.metadata);
return None;
}
let gateways = gateways_res.nodes;
.get_all_basic_entry_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network gateways: {err}"))
.ok()?;
debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
@@ -149,24 +130,12 @@ impl NymApiTopologyProvider {
gateways.len()
);
let mut nodes = Vec::new();
for mix in mixnodes {
if mix.performance.round_to_integer() >= self.config.min_mixnode_performance {
nodes.push(mix)
}
}
for gateway in gateways {
if gateway.performance.round_to_integer() >= self.config.min_gateway_performance {
nodes.push(gateway)
}
}
NymTopology::new(
NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id),
rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes)
topology.add_additional_nodes(mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}));
topology.add_additional_nodes(gateways.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_gateway_performance
}));
};
if !topology.is_minimally_routable() {
@@ -211,7 +211,7 @@ impl<T> TransmissionBuffer<T> {
};
let msg = self.pop_front_from_lane(&lane)?;
log::trace!("picking to send from lane: {lane:?}");
log::trace!("picking to send from lane: {:?}", lane);
Some((lane, msg))
}
+1 -1
View File
@@ -4,6 +4,6 @@
pub use nym_client_core_config_types::disk_persistence;
pub use nym_client_core_config_types::old::{
old_config_v1_1_13, old_config_v1_1_20, old_config_v1_1_20_2, old_config_v1_1_30,
old_config_v1_1_33, old_config_v1_1_54,
old_config_v1_1_33,
};
pub use nym_client_core_config_types::*;
+5 -10
View File
@@ -18,7 +18,7 @@ pub enum ClientCoreError {
#[error("gateway client error ({gateway_id}): {source}")]
GatewayClientError {
gateway_id: String,
source: Box<GatewayClientError>,
source: GatewayClientError,
},
#[error("custom gateway client error: {source}")]
@@ -88,7 +88,10 @@ pub enum ClientCoreError {
},
#[error("failed to establish connection to gateway: {source}")]
GatewayConnectionFailure { source: Box<tungstenite::Error> },
GatewayConnectionFailure {
#[from]
source: tungstenite::Error,
},
#[cfg(target_arch = "wasm32")]
#[error("failed to establish gateway connection (wasm)")]
@@ -224,14 +227,6 @@ pub enum ClientCoreError {
HkdfDerivationError {},
}
impl From<tungstenite::Error> for ClientCoreError {
fn from(err: tungstenite::Error) -> ClientCoreError {
ClientCoreError::GatewayConnectionFailure {
source: Box::new(err),
}
}
}
/// Set of messages that the client can send to listeners via the task manager
#[derive(Debug)]
pub enum ClientCoreStatusMessage {
+6 -6
View File
@@ -107,10 +107,10 @@ pub async fn gateways_for_init<R: Rng>(
log::debug!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_all_basic_entry_assigned_nodes_v2().await?.nodes;
let gateways = client.get_all_basic_entry_assigned_nodes().await?;
info!("nym api reports {} gateways", gateways.len());
log::trace!("Gateways: {gateways:#?}");
log::trace!("Gateways: {:#?}", gateways);
// filter out gateways below minimum performance and ones that could operate as a mixnode
// (we don't want instability)
@@ -121,7 +121,7 @@ pub async fn gateways_for_init<R: Rng>(
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>();
log::debug!("After checking validity: {}", valid_gateways.len());
log::trace!("Valid gateways: {valid_gateways:#?}");
log::trace!("Valid gateways: {:#?}", valid_gateways);
log::info!(
"and {} after validity and performance filtering",
@@ -286,7 +286,7 @@ pub(super) fn get_specified_gateway(
gateways: &[RoutingNode],
must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> {
log::debug!("Requesting specified gateway: {gateway_identity}");
log::debug!("Requesting specified gateway: {}", gateway_identity);
let user_gateway = ed25519::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
@@ -329,7 +329,7 @@ pub(super) async fn register_with_gateway(
log::warn!("Failed to establish connection with gateway!");
ClientCoreError::GatewayClientError {
gateway_id: gateway_id.to_base58_string(),
source: Box::new(err),
source: err,
}
})?;
let auth_response = gateway_client
@@ -339,7 +339,7 @@ pub(super) async fn register_with_gateway(
log::warn!("Failed to register with the gateway {gateway_id}: {err}");
ClientCoreError::GatewayClientError {
gateway_id: gateway_id.to_base58_string(),
source: Box::new(err),
source: err,
}
})?;
+1 -1
View File
@@ -232,7 +232,7 @@ where
} => {
log::debug!("GatewaySetup::ReuseConnection");
Ok(reuse_gateway_connection(
*authenticated_ephemeral_client,
authenticated_ephemeral_client,
*gateway_details,
managed_keys,
))
+2 -2
View File
@@ -218,7 +218,7 @@ pub enum GatewaySetup {
ReuseConnection {
/// The authenticated ephemeral client that was created during `init`
authenticated_ephemeral_client: Box<InitGatewayClient>,
authenticated_ephemeral_client: InitGatewayClient,
// Details of this pre-initialised client (i.e. gateway and keys)
gateway_details: Box<GatewayRegistration>,
@@ -261,7 +261,7 @@ impl GatewaySetup {
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
Ok(GatewaySetup::ReuseConnection {
authenticated_ephemeral_client: Box::new(authenticated_ephemeral_client),
authenticated_ephemeral_client,
gateway_details: Box::new(init_res.gateway_registration),
client_keys: init_res.client_keys,
})
+1 -12
View File
@@ -17,26 +17,15 @@ nym-crypto = { path = "../../crypto", optional = true, default-features = false
nym-sphinx = { path = "../../nymsphinx" }
nym-task = { path = "../../task" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
workspace = true
features = ["fs"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx-pool-guard]
path = "../../../sqlx-pool-guard"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
"macros",
"migrate",
] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
fs-surb-storage = ["sqlx", "nym-crypto", "nym-crypto/hashing"]
@@ -1,8 +0,0 @@
/*
* Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
-- default value of 0 implies 'unknown' variant
ALTER TABLE reply_surb
ADD COLUMN encoded_key_rotation TINYINT NOT NULL DEFAULT 0;
@@ -1,7 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::{io, path::PathBuf};
use std::io;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -29,6 +30,7 @@ pub enum StorageError {
#[error("failed to perform sqlx migration: {source}")]
MigrationError {
#[source]
#[from]
source: sqlx::migrate::MigrateError,
},
@@ -41,6 +43,7 @@ pub enum StorageError {
#[error("failed to run the SQL query: {source}")]
QueryError {
#[source]
#[from]
source: sqlx::error::Error,
},
@@ -15,11 +15,9 @@ use sqlx::{
};
use std::path::Path;
use sqlx_pool_guard::SqlitePoolGuard;
#[derive(Debug, Clone)]
pub struct StorageManager {
connection_pool: SqlitePoolGuard,
pub connection_pool: sqlx::SqlitePool,
}
// all SQL goes here
@@ -39,7 +37,7 @@ impl StorageManager {
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(&database_path)
.filename(database_path)
.create_if_missing(fresh)
.disable_statement_logging();
@@ -51,15 +49,11 @@ impl StorageManager {
}
};
let connection_pool =
SqlitePoolGuard::new(database_path.as_ref().to_path_buf(), connection_pool);
if let Err(err) = sqlx::migrate!("./fs_surbs_migrations")
.run(&*connection_pool)
.run(&connection_pool)
.await
{
error!("Failed to initialize SQLx database: {err}");
connection_pool.close().await;
return Err(err.into());
}
@@ -67,43 +61,38 @@ impl StorageManager {
Ok(StorageManager { connection_pool })
}
/// Close connection pool waiting for all connections to be closed.
pub async fn close_pool(&self) {
self.connection_pool.close().await;
}
#[allow(dead_code)]
pub async fn status_table_exists(&self) -> Result<bool, sqlx::Error> {
sqlx::query!("SELECT name FROM sqlite_master WHERE type='table' AND name='status'")
.fetch_optional(&*self.connection_pool)
.fetch_optional(&self.connection_pool)
.await
.map(|r| r.is_some())
}
pub async fn create_status_table(&self) -> Result<(), sqlx::Error> {
sqlx::query!("INSERT INTO status(flush_in_progress, previous_flush_timestamp, client_in_use) VALUES (0, 0, 1)")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_flush_status(&self) -> Result<bool, sqlx::Error> {
sqlx::query!("SELECT flush_in_progress FROM status;")
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
.map(|r| r.flush_in_progress > 0)
}
pub async fn set_previous_flush_timestamp(&self, timestamp: i64) -> Result<(), sqlx::Error> {
sqlx::query!("UPDATE status SET previous_flush_timestamp = ?", timestamp)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_previous_flush_timestamp(&self) -> Result<i64, sqlx::Error> {
sqlx::query!("SELECT previous_flush_timestamp FROM status;")
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
.map(|r| r.previous_flush_timestamp)
}
@@ -111,14 +100,14 @@ impl StorageManager {
pub async fn set_flush_status(&self, in_progress: bool) -> Result<(), sqlx::Error> {
let in_progress_int = i64::from(in_progress);
sqlx::query!("UPDATE status SET flush_in_progress = ?", in_progress_int)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_client_in_use_status(&self) -> Result<bool, sqlx::Error> {
sqlx::query!("SELECT client_in_use FROM status;")
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
.map(|r| r.client_in_use > 0)
}
@@ -126,21 +115,21 @@ impl StorageManager {
pub async fn set_client_in_use_status(&self, in_use: bool) -> Result<(), sqlx::Error> {
let in_use_int = i64::from(in_use);
sqlx::query!("UPDATE status SET client_in_use = ?", in_use_int)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn delete_all_tags(&self) -> Result<(), sqlx::Error> {
sqlx::query!("DELETE FROM sender_tag;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_tags(&self) -> Result<Vec<StoredSenderTag>, sqlx::Error> {
sqlx::query_as!(StoredSenderTag, "SELECT * FROM sender_tag;",)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -152,21 +141,21 @@ impl StorageManager {
stored_tag.recipient,
stored_tag.tag
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn delete_all_reply_keys(&self) -> Result<(), sqlx::Error> {
sqlx::query!("DELETE FROM reply_key;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_reply_keys(&self) -> Result<Vec<StoredReplyKey>, sqlx::Error> {
sqlx::query_as!(StoredReplyKey, "SELECT * FROM reply_key;",)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -182,14 +171,14 @@ impl StorageManager {
stored_reply_key.reply_key,
stored_reply_key.sent_at_timestamp
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_surb_senders(&self) -> Result<Vec<StoredSurbSender>, sqlx::Error> {
sqlx::query_as!(StoredSurbSender, "SELECT * FROM reply_surb_sender;",)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -204,7 +193,7 @@ impl StorageManager {
stored_surb_sender.tag,
stored_surb_sender.last_sent_timestamp
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?
.last_insert_rowid();
Ok(id)
@@ -216,23 +205,20 @@ impl StorageManager {
) -> Result<Vec<StoredReplySurb>, sqlx::Error> {
sqlx::query_as!(
StoredReplySurb,
r#"
SELECT reply_surb_sender_id, reply_surb, encoded_key_rotation as "encoded_key_rotation: u8" FROM reply_surb
WHERE reply_surb_sender_id = ?
"#,
"SELECT * FROM reply_surb WHERE reply_surb_sender_id = ?",
sender_id
)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
pub async fn delete_all_reply_surb_data(&self) -> Result<(), sqlx::Error> {
sqlx::query!("DELETE FROM reply_surb;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
sqlx::query!("DELETE FROM reply_surb_sender;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
@@ -244,13 +230,12 @@ impl StorageManager {
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO reply_surb(reply_surb_sender_id, reply_surb, encoded_key_rotation) VALUES (?, ?, ?);
INSERT INTO reply_surb(reply_surb_sender_id, reply_surb) VALUES (?, ?);
"#,
stored_reply_surb.reply_surb_sender_id,
stored_reply_surb.reply_surb,
stored_reply_surb.encoded_key_rotation
stored_reply_surb.reply_surb
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
@@ -264,7 +249,7 @@ impl StorageManager {
SELECT min_reply_surb_threshold as "min_reply_surb_threshold: u32", max_reply_surb_threshold as "max_reply_surb_threshold: u32" FROM reply_surb_storage_metadata;
"#,
)
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
}
@@ -278,7 +263,7 @@ impl StorageManager {
"#,
metadata.min_reply_surb_threshold,
metadata.max_reply_surb_threshold,
).execute(&*self.connection_pool).await?;
).execute(&self.connection_pool).await?;
Ok(())
}
}
@@ -1,21 +1,18 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::backend::fs_backend::manager::StorageManager;
use crate::backend::fs_backend::models::{
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
};
use crate::surb_storage::ReceivedReplySurbs;
use crate::{
backend::fs_backend::{
manager::StorageManager,
models::{
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag,
StoredSurbSender,
},
},
surb_storage::ReceivedReplySurbs,
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys,
UsedSenderTags,
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys, UsedSenderTags,
};
use async_trait::async_trait;
use log::{debug, error, info, warn};
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use std::fs;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;
@@ -44,17 +41,15 @@ impl Backend {
}
let manager = StorageManager::init(database_path, true).await?;
match manager.create_status_table().await {
Ok(()) => Ok(Backend {
temporary_old_path: None,
database_path: owned_path,
manager,
}),
Err(err) => {
manager.close_pool().await;
Err(err.into())
}
}
manager.create_status_table().await?;
let backend = Backend {
temporary_old_path: None,
database_path: owned_path,
manager,
};
Ok(backend)
}
pub async fn try_load<P: AsRef<Path>>(
@@ -69,28 +64,7 @@ impl Backend {
}
let manager = StorageManager::init(database_path, false).await?;
match Self::try_load_inner(&manager, fresh_sender_tags).await {
Ok(()) => Ok(Backend {
temporary_old_path: None,
database_path: owned_path,
manager,
}),
Err(e) => {
manager.close_pool().await;
Err(e)
}
}
}
/// Gracefully close sqlite connection pool and drop backend.
pub async fn shutdown(self) {
self.manager.close_pool().await
}
async fn try_load_inner(
manager: &StorageManager,
fresh_sender_tags: bool,
) -> Result<(), StorageError> {
// the database flush wasn't fully finished and thus the data is in inconsistent state
// (we don't really know what's properly saved or what's not)
if manager.get_flush_status().await? {
@@ -152,11 +126,20 @@ impl Backend {
manager.delete_all_tags().await?;
}
Ok(())
Ok(Backend {
temporary_old_path: None,
database_path: owned_path,
// manager: StorageManagerState::Storage(manager),
manager,
})
}
async fn close_pool(&mut self) {
self.manager.connection_pool.close().await;
}
async fn rotate(&mut self) -> Result<(), StorageError> {
self.manager.close_pool().await;
self.close_pool().await;
let new_extension = if let Some(existing_extension) =
self.database_path.extension().and_then(|ext| ext.to_str())
@@ -169,8 +152,7 @@ impl Backend {
let mut temp_old = self.database_path.clone();
temp_old.set_extension(new_extension);
tokio::fs::rename(&self.database_path, &temp_old)
.await
fs::rename(&self.database_path, &temp_old)
.map_err(|err| StorageError::DatabaseRenameError { source: err })?;
self.manager = StorageManager::init(&self.database_path, true).await?;
self.manager.create_status_table().await?;
@@ -179,10 +161,9 @@ impl Backend {
Ok(())
}
async fn remove_old(&mut self) -> Result<(), StorageError> {
fn remove_old(&mut self) -> Result<(), StorageError> {
if let Some(old_path) = self.temporary_old_path.take() {
tokio::fs::remove_file(old_path)
.await
fs::remove_file(old_path)
.map_err(|err| StorageError::DatabaseOldFileRemoveError { source: err })
} else {
warn!("the old database file doesn't seem to exist!");
@@ -354,7 +335,7 @@ impl ReplyStorageBackend for Backend {
self.dump_reply_surb_storage_metadata(surbs_ref).await?;
self.dump_reply_surbs(surbs_ref).await?;
self.remove_old().await?;
self.remove_old()?;
self.end_storage_flush().await
}
@@ -8,10 +8,8 @@ use nym_crypto::Digest;
use nym_sphinx::addressing::clients::{Recipient, RecipientBytes};
use nym_sphinx::anonymous_replies::encryption_key::EncryptionKeyDigest;
use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE};
use nym_sphinx::anonymous_replies::{
ReplySurb, ReplySurbWithKeyRotation, SurbEncryptionKey, SurbEncryptionKeySize,
};
use nym_sphinx::params::{ReplySurbKeyDigestAlgorithm, SphinxKeyRotation};
use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey, SurbEncryptionKeySize};
use nym_sphinx::params::ReplySurbKeyDigestAlgorithm;
#[derive(Debug, Clone)]
pub struct StoredSenderTag {
@@ -148,40 +146,24 @@ impl TryFrom<StoredSurbSender> for (AnonymousSenderTag, i64) {
pub struct StoredReplySurb {
pub reply_surb_sender_id: i64,
pub reply_surb: Vec<u8>,
// encodes only whether it's 'even', 'odd' or 'unknown' (default)
// and not the whole id because that's redundant
pub encoded_key_rotation: u8,
}
impl StoredReplySurb {
pub fn new(reply_surb_sender_id: i64, reply_surb: &ReplySurbWithKeyRotation) -> Self {
pub fn new(reply_surb_sender_id: i64, reply_surb: &ReplySurb) -> Self {
StoredReplySurb {
reply_surb_sender_id,
reply_surb: reply_surb.inner_reply_surb().to_bytes(),
encoded_key_rotation: reply_surb.key_rotation() as u8,
reply_surb: reply_surb.to_bytes(),
}
}
}
impl TryFrom<StoredReplySurb> for ReplySurbWithKeyRotation {
impl TryFrom<StoredReplySurb> for ReplySurb {
type Error = StorageError;
fn try_from(value: StoredReplySurb) -> Result<Self, Self::Error> {
let key_rotation =
SphinxKeyRotation::try_from(value.encoded_key_rotation).map_err(|err| {
StorageError::CorruptedData {
details: format!("stored key rotation was malformed: {err}"),
}
})?;
let reply_surb = ReplySurb::from_bytes(&value.reply_surb).map_err(|err| {
StorageError::CorruptedData {
details: format!("failed to recover the reply surb: {err}"),
}
})?;
Ok(reply_surb.with_key_rotation(key_rotation))
ReplySurb::from_bytes(&value.reply_surb).map_err(|err| StorageError::CorruptedData {
details: format!("failed to recover the reply surb: {err}"),
})
}
}
@@ -33,6 +33,7 @@ where
self.backend.load_surb_storage().await
}
// this will have to get enabled after merging develop
pub async fn flush_on_shutdown(
mut self,
mem_state: CombinedReplyStorage,
@@ -49,6 +50,7 @@ where
shutdown.recv().await;
info!("PersistentReplyStorage is flushing all reply-related data to underlying storage");
info!("you MUST NOT forcefully shutdown now or you risk data corruption!");
if let Err(err) = self.backend.flush_surb_storage(&mem_state).await {
error!("failed to flush our reply-related data to the persistent storage: {err}")
} else {
@@ -5,7 +5,7 @@ use dashmap::iter::Iter;
use dashmap::DashMap;
use log::trace;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation;
use nym_sphinx::anonymous_replies::ReplySurb;
use std::collections::VecDeque;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
@@ -134,7 +134,7 @@ impl ReceivedReplySurbsMap {
&self,
target: &AnonymousSenderTag,
amount: usize,
) -> (Option<Vec<ReplySurbWithKeyRotation>>, usize) {
) -> (Option<Vec<ReplySurb>>, usize) {
if let Some(mut entry) = self.inner.data.get_mut(target) {
let surbs_left = entry.items_left();
if surbs_left < self.min_surb_threshold() + amount {
@@ -150,7 +150,7 @@ impl ReceivedReplySurbsMap {
pub fn get_reply_surb_ignoring_threshold(
&self,
target: &AnonymousSenderTag,
) -> Option<(Option<ReplySurbWithKeyRotation>, usize)> {
) -> Option<(Option<ReplySurb>, usize)> {
self.inner
.data
.get_mut(target)
@@ -160,7 +160,7 @@ impl ReceivedReplySurbsMap {
pub fn get_reply_surb(
&self,
target: &AnonymousSenderTag,
) -> Option<(Option<ReplySurbWithKeyRotation>, usize)> {
) -> Option<(Option<ReplySurb>, usize)> {
self.inner.data.get_mut(target).map(|mut entry| {
let surbs_left = entry.items_left();
if surbs_left < self.min_surb_threshold() {
@@ -171,7 +171,7 @@ impl ReceivedReplySurbsMap {
})
}
pub fn insert_surbs<I: IntoIterator<Item = ReplySurbWithKeyRotation>>(
pub fn insert_surbs<I: IntoIterator<Item = ReplySurb>>(
&self,
target: &AnonymousSenderTag,
surbs: I,
@@ -189,14 +189,14 @@ impl ReceivedReplySurbsMap {
pub struct ReceivedReplySurbs {
// in the future we'd probably want to put extra data here to indicate when the SURBs got received
// so we could invalidate entries from the previous key rotations
data: VecDeque<ReplySurbWithKeyRotation>,
data: VecDeque<ReplySurb>,
pending_reception: u32,
surbs_last_received_at_timestamp: i64,
}
impl ReceivedReplySurbs {
fn new(initial_surbs: VecDeque<ReplySurbWithKeyRotation>) -> Self {
fn new(initial_surbs: VecDeque<ReplySurb>) -> Self {
ReceivedReplySurbs {
data: initial_surbs,
pending_reception: 0,
@@ -206,7 +206,7 @@ impl ReceivedReplySurbs {
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
pub fn new_retrieved(
surbs: Vec<ReplySurbWithKeyRotation>,
surbs: Vec<ReplySurb>,
surbs_last_received_at_timestamp: i64,
) -> ReceivedReplySurbs {
ReceivedReplySurbs {
@@ -217,7 +217,7 @@ impl ReceivedReplySurbs {
}
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
pub fn surbs_ref(&self) -> &VecDeque<ReplySurbWithKeyRotation> {
pub fn surbs_ref(&self) -> &VecDeque<ReplySurb> {
&self.data
}
@@ -243,10 +243,7 @@ impl ReceivedReplySurbs {
self.pending_reception = 0;
}
pub fn get_reply_surbs(
&mut self,
amount: usize,
) -> (Option<Vec<ReplySurbWithKeyRotation>>, usize) {
pub fn get_reply_surbs(&mut self, amount: usize) -> (Option<Vec<ReplySurb>>, usize) {
if self.items_left() < amount {
(None, self.items_left())
} else {
@@ -255,11 +252,11 @@ impl ReceivedReplySurbs {
}
}
pub fn get_reply_surb(&mut self) -> (Option<ReplySurbWithKeyRotation>, usize) {
pub fn get_reply_surb(&mut self) -> (Option<ReplySurb>, usize) {
(self.pop_surb(), self.items_left())
}
fn pop_surb(&mut self) -> Option<ReplySurbWithKeyRotation> {
fn pop_surb(&mut self) -> Option<ReplySurb> {
self.data.pop_front()
}
@@ -268,10 +265,7 @@ impl ReceivedReplySurbs {
}
// realistically we're always going to be getting multiple surbs at once
pub fn insert_reply_surbs<I: IntoIterator<Item = ReplySurbWithKeyRotation>>(
&mut self,
surbs: I,
) {
pub fn insert_reply_surbs<I: IntoIterator<Item = ReplySurb>>(&mut self, surbs: I) {
let mut v = surbs.into_iter().collect::<VecDeque<_>>();
trace!("storing {} surbs in the storage", v.len());
self.data.append(&mut v);
@@ -21,8 +21,8 @@ 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,
SensitiveServerResponse, ServerResponse, SharedGatewayKey, SharedSymmetricKey,
CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION,
};
use nym_sphinx::forwarding::packet::MixPacket;
use nym_statistics_common::clients::connection::ConnectionStatsEvent;
@@ -662,7 +662,6 @@ impl<C, St> GatewayClient<C, St> {
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();
if !supports_aes_gcm_siv {
warn!("this gateway is on an old version that doesn't support AES256-GCM-SIV");
@@ -670,9 +669,6 @@ impl<C, St> GatewayClient<C, St> {
if !supports_auth_v2 {
warn!("this gateway is on an old version that doesn't support authentication v2")
}
if !supports_key_rotation_info {
warn!("this gateway is on an old version that doesn't support key rotation packets")
}
if self.authenticated {
debug!("Already authenticated");
@@ -853,22 +849,6 @@ impl<C, St> GatewayClient<C, St> {
}
}
fn mix_packet_to_ws_message(&self, packet: MixPacket) -> Result<Message, GatewayRequestsError> {
// note: into_ws_message encrypts the requests and adds a MAC on it. Perhaps it should
// be more explicit in the naming?
let req = if self.negotiated_protocol.supports_key_rotation_packet() {
BinaryRequest::ForwardSphinxV2 { packet }
} else {
BinaryRequest::ForwardSphinx { packet }
};
req.into_ws_message(
self.shared_key
.as_ref()
.expect("no shared key present even though we're authenticated!"),
)
}
pub async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
@@ -897,7 +877,13 @@ impl<C, St> GatewayClient<C, St> {
let messages: Result<Vec<_>, _> = packets
.into_iter()
.map(|mix_packet| self.mix_packet_to_ws_message(mix_packet))
.map(|mix_packet| {
BinaryRequest::ForwardSphinx { packet: mix_packet }.into_ws_message(
self.shared_key
.as_ref()
.expect("no shared key present even though we're authenticated!"),
)
})
.collect();
if let Err(err) = self
@@ -963,8 +949,13 @@ impl<C, St> GatewayClient<C, St> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
let msg = self.mix_packet_to_ws_message(mix_packet)?;
// note: into_ws_message encrypts the requests and adds a MAC on it. Perhaps it should
// be more explicit in the naming?
let msg = BinaryRequest::ForwardSphinx { packet: mix_packet }.into_ws_message(
self.shared_key
.as_ref()
.expect("no shared key present even though we're authenticated!"),
)?;
self.send_with_reconnection_on_failure(msg).await
}
@@ -56,7 +56,7 @@ pub(crate) async fn connect_async(
}
.map_err(|err| GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: Box::new(tungstenite::Error::from(err)),
source: err.into(),
})?;
#[cfg(unix)]
@@ -72,7 +72,7 @@ pub(crate) async fn connect_async(
Err(err) => {
stream = Err(GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: Box::new(tungstenite::Error::from(err)),
source: err.into(),
});
continue;
}
@@ -83,6 +83,6 @@ pub(crate) async fn connect_async(
.await
.map_err(|error| GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: Box::new(error),
source: error,
})
}
+3 -12
View File
@@ -25,7 +25,7 @@ pub enum GatewayClientError {
RequestError(#[from] GatewayRequestsError),
#[error("There was a network error: {0}")]
NetworkError(Box<WsError>),
NetworkError(#[from] WsError),
#[error("failed to upgrade our shared key - the gateway sent malformed response")]
FatalKeyUpgradeFailure,
@@ -41,10 +41,7 @@ pub enum GatewayClientError {
NetworkErrorWasm(#[from] JsError),
#[error("connection failed: {address}: {source}")]
NetworkConnectionFailed {
address: String,
source: Box<WsError>,
},
NetworkConnectionFailed { address: String, source: WsError },
#[error("no socket address for endpoint: {address}")]
NoEndpointForConnection { address: String },
@@ -130,16 +127,10 @@ pub enum GatewayClientError {
ShutdownInProgress,
}
impl From<WsError> for GatewayClientError {
fn from(error: WsError) -> Self {
GatewayClientError::NetworkError(Box::new(error))
}
}
impl GatewayClientError {
pub fn is_closed_connection(&self) -> bool {
match self {
GatewayClientError::NetworkError(ws_err) => match ws_err.as_ref() {
GatewayClientError::NetworkError(ws_err) => match ws_err {
WsError::AlreadyClosed | WsError::ConnectionClosed => true,
WsError::Io(io_err) => matches!(
io_err.kind(),
+2 -2
View File
@@ -28,7 +28,7 @@ pub(crate) fn cleanup_socket_message(
msg: Option<Result<Message, WsError>>,
) -> Result<Message, GatewayClientError> {
match msg {
Some(msg) => msg.map_err(GatewayClientError::from),
Some(msg) => msg.map_err(GatewayClientError::NetworkError),
None => Err(GatewayClientError::ConnectionAbruptlyClosed),
}
}
@@ -39,7 +39,7 @@ pub(crate) fn cleanup_socket_messages(
match msgs {
Some(msgs) => msgs
.into_iter()
.map(|msg| msg.map_err(GatewayClientError::from))
.map(|msg| msg.map_err(GatewayClientError::NetworkError))
.collect(),
None => Err(GatewayClientError::ConnectionAbruptlyClosed),
}
@@ -10,7 +10,7 @@ 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::{ServerResponse, SimpleGatewayRequestsError};
use nym_task::TaskClient;
use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd;
@@ -188,34 +188,6 @@ impl PartiallyDelegatedRouter {
}
}
}
ServerResponse::EncryptedResponse { ciphertext, nonce } => {
match SensitiveServerResponse::decrypt(
&ciphertext,
&nonce,
self.shared_key.as_ref(),
) {
Ok(response) => match response {
SensitiveServerResponse::ForgetMeAck {} => {
info!("received forget me acknowledgement");
}
SensitiveServerResponse::RememberMeAck {} => {
info!("received remember me acknowledgement");
}
SensitiveServerResponse::KeyUpgradeAck {} => {
warn!(
"received illegal key upgrade acknowledgement in an authenticated client"
);
}
_ => {
warn!("received unknown SensitiveServerResponse");
}
},
Err(e) => {
error!("failed to handle encrypted response: {e}");
}
}
Ok(())
}
other => {
let name = other.name();
warn!("received illegal message of type '{name}' in an authenticated client");
+1 -6
View File
@@ -16,14 +16,9 @@ tokio-util = { workspace = true, features = ["codec"], optional = true }
tokio-stream = { workspace = true }
# internal
nym-noise = { path = "../../nymnoise" }
nym-sphinx = { path = "../../nymsphinx" }
nym-task = { path = "../../task", optional = true }
[features]
default = ["client"]
client = ["tokio-util", "nym-task", "tokio/net", "tokio/rt"]
[dev-dependencies]
nym-crypto = { path = "../../crypto" }
rand = { workspace = true }
client = ["tokio-util", "nym-task", "tokio/net", "tokio/rt"]
+25 -48
View File
@@ -3,11 +3,11 @@
use dashmap::DashMap;
use futures::StreamExt;
use nym_noise::config::NoiseConfig;
use nym_noise::upgrade_noise_initiator;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket;
use nym_sphinx::params::PacketType;
use nym_sphinx::NymPacket;
use std::io;
use std::net::SocketAddr;
use std::ops::Deref;
@@ -49,19 +49,23 @@ impl Config {
pub trait SendWithoutResponse {
// Without response in this context means we will not listen for anything we might get back (not
// that we should get anything), including any possible io errors
fn send_without_response(&self, packet: MixPacket) -> io::Result<()>;
fn send_without_response(
&self,
address: NymNodeRoutingAddress,
packet: NymPacket,
packet_type: PacketType,
) -> io::Result<()>;
}
pub struct Client {
active_connections: ActiveConnections,
noise_config: NoiseConfig,
connections_count: Arc<AtomicUsize>,
config: Config,
}
#[derive(Default, Clone)]
pub struct ActiveConnections {
inner: Arc<DashMap<SocketAddr, ConnectionSender>>,
inner: Arc<DashMap<NymNodeRoutingAddress, ConnectionSender>>,
}
impl ActiveConnections {
@@ -78,7 +82,7 @@ impl ActiveConnections {
}
impl Deref for ActiveConnections {
type Target = DashMap<SocketAddr, ConnectionSender>;
type Target = DashMap<NymNodeRoutingAddress, ConnectionSender>;
fn deref(&self) -> &Self::Target {
&self.inner
}
@@ -100,7 +104,6 @@ impl ConnectionSender {
struct ManagedConnection {
address: SocketAddr,
noise_config: NoiseConfig,
message_receiver: ReceiverStream<FramedNymPacket>,
connection_timeout: Duration,
current_reconnection: Arc<AtomicU32>,
@@ -109,14 +112,12 @@ struct ManagedConnection {
impl ManagedConnection {
fn new(
address: SocketAddr,
noise_config: NoiseConfig,
message_receiver: mpsc::Receiver<FramedNymPacket>,
connection_timeout: Duration,
current_reconnection: Arc<AtomicU32>,
) -> Self {
ManagedConnection {
address,
noise_config,
message_receiver: ReceiverStream::new(message_receiver),
connection_timeout,
current_reconnection,
@@ -131,21 +132,9 @@ impl ManagedConnection {
Ok(stream_res) => match stream_res {
Ok(stream) => {
debug!("Managed to establish connection to {}", self.address);
let noise_stream =
match upgrade_noise_initiator(stream, &self.noise_config).await {
Ok(noise_stream) => noise_stream,
Err(err) => {
error!("Failed to perform Noise handshake with {address} - {err}");
// we failed to finish the noise handshake - increase reconnection attempt
self.current_reconnection.fetch_add(1, Ordering::SeqCst);
return;
}
};
// if we managed to connect AND do the noise handshake, reset the reconnection count (whatever it might have been)
// if we managed to connect, reset the reconnection count (whatever it might have been)
self.current_reconnection.store(0, Ordering::Release);
debug!("Noise initiator handshake completed for {:?}", address);
Framed::new(noise_stream, NymCodec)
Framed::new(stream, NymCodec)
}
Err(err) => {
debug!("failed to establish connection to {address} (err: {err})",);
@@ -178,14 +167,9 @@ impl ManagedConnection {
}
impl Client {
pub fn new(
config: Config,
noise_config: NoiseConfig,
connections_count: Arc<AtomicUsize>,
) -> Client {
pub fn new(config: Config, connections_count: Arc<AtomicUsize>) -> Client {
Client {
active_connections: Default::default(),
noise_config,
connections_count,
config,
}
@@ -212,7 +196,7 @@ impl Client {
}
}
fn make_connection(&self, address: SocketAddr, pending_packet: FramedNymPacket) {
fn make_connection(&self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
let (sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size);
// this CAN'T fail because we just created the channel which has a non-zero capacity
@@ -240,7 +224,6 @@ impl Client {
let initial_connection_timeout = self.config.initial_connection_timeout;
let connections_count = self.connections_count.clone();
let noise_config = self.noise_config.clone();
tokio::spawn(async move {
// before executing the manager, wait for what was specified, if anything
if let Some(backoff) = backoff {
@@ -250,8 +233,7 @@ impl Client {
connections_count.fetch_add(1, Ordering::SeqCst);
ManagedConnection::new(
address,
noise_config,
address.into(),
receiver,
initial_connection_timeout,
current_reconnection_attempt,
@@ -264,14 +246,18 @@ impl Client {
}
impl SendWithoutResponse for Client {
fn send_without_response(&self, packet: MixPacket) -> io::Result<()> {
let address = packet.next_hop_address();
trace!("Sending packet to {address}");
let framed_packet = FramedNymPacket::from(packet);
fn send_without_response(
&self,
address: NymNodeRoutingAddress,
packet: NymPacket,
packet_type: PacketType,
) -> io::Result<()> {
trace!("Sending packet to {address:?}");
let framed_packet = FramedNymPacket::new(packet, packet_type);
let Some(sender) = self.active_connections.get_mut(&address) else {
// there was never a connection to begin with
debug!("establishing initial connection to {address}");
debug!("establishing initial connection to {}", address);
// it's not a 'big' error, but we did not manage to send the packet, but queue the packet
// for sending for as soon as the connection is created
self.make_connection(address, framed_packet);
@@ -316,12 +302,8 @@ impl SendWithoutResponse for Client {
#[cfg(test)]
mod tests {
use super::*;
use nym_crypto::asymmetric::x25519;
use nym_noise::config::NoiseNetworkView;
use rand::rngs::OsRng;
fn dummy_client() -> Client {
let mut rng = OsRng; //for test only, so we don't care if rng source isn't crypto grade
Client::new(
Config {
initial_reconnection_backoff: Duration::from_millis(10_000),
@@ -329,11 +311,6 @@ mod tests {
initial_connection_timeout: Duration::from_millis(1_500),
maximum_connection_buffer_size: 128,
},
NoiseConfig::new(
Arc::new(x25519::KeyPair::new(&mut rng)),
NoiseNetworkView::new_empty(),
Duration::from_millis(1_500),
),
Default::default(),
)
}
@@ -19,7 +19,6 @@ nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-c
nym-ecash-contract-common = { path = "../../cosmwasm-smart-contracts/ecash-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-performance-contract-common = { path = "../../cosmwasm-smart-contracts/nym-performance-contract" }
nym-serde-helpers = { path = "../../serde-helpers", features = ["hex", "base64"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
@@ -16,8 +16,8 @@ async fn main() {
let prefix = "n";
let denom: Denom = "unym".parse().unwrap();
let signer_mnemonic: bip39::Mnemonic = "<MNEMONIC WITH FUNDS HERE>".parse().unwrap();
let validator = "https://rpc.sandbox.nymtech.net";
let to_address: AccountId = "n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa".parse().unwrap();
let validator = "https://qwerty-validator.qa.nymte.ch";
let to_address: AccountId = "n19kdst4srf76xgwe55jg32mpcpcyf6aqgp6qrdk".parse().unwrap();
let signer = DirectSecp256k1HdWallet::from_mnemonic(prefix, signer_mnemonic);
let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone();
+87 -155
View File
@@ -25,9 +25,7 @@ use nym_api_requests::models::{
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
};
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::nym_nodes::{
NodesByAddressesResponse, SemiSkimmedNodesWithMetadata, SkimmedNode, SkimmedNodesWithMetadata,
};
use nym_api_requests::nym_nodes::{NodesByAddressesResponse, SkimmedNode};
use nym_coconut_dkg_common::types::EpochId;
use nym_http_api_client::UserAgent;
use nym_mixnet_contract_common::EpochRewardedSet;
@@ -48,46 +46,6 @@ use crate::rpc::http_client;
#[cfg(feature = "http-client")]
use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient};
// a simple helper macro to define to repeatedly call a paged query until a full response is constructed
macro_rules! collect_paged_skimmed_v2 {
( $self:ident, $f: ident ) => {{
// unroll first loop iteration in order to obtain the metadata
let mut page = 0;
let res = $self
.nym_api
.$f(false, Some(page), None, $self.use_bincode)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let mut res = $self
.nym_api
.$f(false, Some(page), None, $self.use_bincode)
.await?;
if metadata != res.metadata {
return Err(ValidatorClientError::InconsistentPagedMetadata);
}
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}};
}
#[must_use]
#[derive(Debug, Clone)]
pub struct Config {
@@ -242,11 +200,11 @@ impl<C, S> Client<C, S> {
#[allow(deprecated)]
impl<C, S> Client<C, S> {
pub fn api_url(&self) -> &Url {
self.nym_api.current_url().as_ref()
self.nym_api.current_url()
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_base_urls(vec![new_endpoint.into()])
self.nym_api.change_base_url(new_endpoint)
}
#[deprecated]
@@ -387,47 +345,25 @@ impl<C, S> Client<C, S> {
#[derive(Clone)]
pub struct NymApiClient {
pub use_bincode: bool,
pub nym_api: nym_api::Client,
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
// we could re-implement the communication with the REST API on port 1317
}
impl From<nym_api::Client> for NymApiClient {
fn from(nym_api: nym_api::Client) -> Self {
NymApiClient {
use_bincode: false,
nym_api,
}
}
}
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
#[allow(deprecated)]
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url, None);
NymApiClient {
use_bincode: true,
nym_api,
}
NymApiClient { nym_api }
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
let nym_api = nym_api::Client::new(api_url, Some(timeout));
NymApiClient {
use_bincode: true,
nym_api,
}
}
#[must_use]
pub fn with_bincode(mut self, use_bincode: bool) -> Self {
self.use_bincode = use_bincode;
self
NymApiClient { nym_api }
}
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
@@ -437,18 +373,15 @@ impl NymApiClient {
.build::<ValidatorClientError>()
.expect("failed to build nym api client");
NymApiClient {
use_bincode: false,
nym_api,
}
NymApiClient { nym_api }
}
pub fn api_url(&self) -> &Url {
self.nym_api.current_url().as_ref()
self.nym_api.current_url()
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_base_urls(vec![new_endpoint.into()]);
self.nym_api.change_base_url(new_endpoint);
}
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes instead")]
@@ -467,93 +400,17 @@ impl NymApiClient {
/// retrieve basic information for nodes are capable of operating as an entry gateway
/// this includes legacy gateways and nym-nodes
#[deprecated(note = "use get_all_basic_entry_assigned_nodes_with_metadata instead")]
pub async fn get_all_basic_entry_assigned_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_entry_assigned_nodes_v2()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_entry_assigned_nodes_v2(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
collect_paged_skimmed_v2!(self, get_basic_entry_assigned_nodes_v2)
}
/// retrieve basic information for nodes that got assigned 'mixing' node in this epoch
/// this includes legacy mixnodes and nym-nodes
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes_with_metadata instead")]
pub async fn get_all_basic_active_mixing_assigned_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_active_mixing_assigned_nodes_with_metadata()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_active_mixing_assigned_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
collect_paged_skimmed_v2!(self, get_basic_active_mixing_assigned_nodes_v2)
}
/// retrieve basic information for nodes are capable of operating as a mixnode
/// this includes legacy mixnodes and nym-nodes
#[deprecated(note = "use get_all_basic_mixing_capable_nodes_with_metadata instead")]
pub async fn get_all_basic_mixing_capable_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_mixing_capable_nodes_with_metadata()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_mixing_capable_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
collect_paged_skimmed_v2!(self, get_basic_mixing_capable_nodes_v2)
}
/// retrieve basic information for all bonded nodes on the network
#[deprecated(note = "use get_all_basic_nodes_with_metadata instead")]
pub async fn get_all_basic_nodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_nodes_with_metadata()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
collect_paged_skimmed_v2!(self, get_basic_nodes_v2)
}
/// retrieve expanded information for all bonded nodes on the network
pub async fn get_all_expanded_nodes(
&self,
) -> Result<SemiSkimmedNodesWithMetadata, ValidatorClientError> {
// Unroll the first iteration to get the metadata
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let res = self
.nym_api
.get_expanded_nodes(false, Some(page), None)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
let mut nodes = Vec::new();
loop {
let mut res = self
.nym_api
.get_expanded_nodes(false, Some(page), None)
.get_basic_entry_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -564,7 +421,82 @@ impl NymApiClient {
}
}
Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata))
Ok(nodes)
}
/// retrieve basic information for nodes that got assigned 'mixing' node in this epoch
/// this includes legacy mixnodes and nym-nodes
pub async fn get_all_basic_active_mixing_assigned_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
loop {
let mut res = self
.nym_api
.get_basic_active_mixing_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(nodes)
}
/// retrieve basic information for nodes are capable of operating as a mixnode
/// this includes legacy mixnodes and nym-nodes
pub async fn get_all_basic_mixing_capable_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
loop {
let mut res = self
.nym_api
.get_basic_mixing_capable_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(nodes)
}
/// retrieve basic information for all bonded nodes on the network
pub async fn get_all_basic_nodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
loop {
let mut res = self
.nym_api
.get_basic_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(nodes)
}
pub async fn health(&self) -> Result<ApiHealthResponse, ValidatorClientError> {

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