Compare commits

...

54 Commits

Author SHA1 Message Date
Andrej Mihajlov efb2aed9dd Update Cargo.toml 2024-12-10 15:30:52 +01:00
Andrej Mihajlov aaec2c9e82 Update Cargo.toml 2024-12-10 15:26:40 +01:00
Andrej Mihajlov f6a6601495 Add feat zeroize to cipher 2024-12-10 15:18:14 +01:00
Jędrzej Stuczyński 66fea38d20 bugfix: make sure to update timestamp of last batch verification to prevent double redemption (#5239) 2024-12-10 13:35:29 +00:00
benedetta davico c29fce0856 Update NS-api version in Cargo.toml 2024-12-10 11:16:16 +01:00
Jon Häggblad 33bdf08804 Add FromStr impl for UserAgent (#5236)
* Add FromStr impl for UserAgent

* Convert error type to struct
2024-12-10 10:35:19 +01:00
dependabot[bot] 236555e6c1 build(deps): bump mikefarah/yq from 4.44.5 to 4.44.6 (#5234) 2024-12-09 22:58:46 +01:00
Jon Häggblad c54760bb0b TicketType derive Hash and Eq (#5233) 2024-12-09 22:53:52 +01:00
Drazen Urch 1b8a929ff5 Nmv2 add debug config (#5212)
* Add debug config to clients

* Add deterministic traffic selection flag
2024-12-09 09:03:04 +01:00
Jon Häggblad 72a4624ace Add NYXD_WS to qa.env (#5226) 2024-12-09 09:00:39 +01:00
Mark Sinclair e5e7ddb0b6 Create push-nyx-chain-watcher.yaml 2024-12-06 20:30:19 +00:00
Jędrzej Stuczyński 5a07b73375 feature: hopefully final steps of the smoosh™️ (#5201)
* removed mnemonic from gateway config struct

scaffolding for common mixnet listener

running verloc unconditionally in a nym-node

remove filtering by mixnode

extracted verloc to separate crate

integrated nym-node-http-server more tightly with the binary

most logic for handling forward packets

running all mixnode-related tasks natively inside nymnode

removed gateway storage trait in favour of the only concrete implementation

most logic for handling final hop packets

using nym-node owned socket listener for gateways

utility for sending plain message through mixnet + gateway fix

using common packet forwarding in both modes

nifying nym-node metrics

reproduce behaviour of the console logger

cleaned up cli args

redesigned gateway tasks startup procedure

removing dead code

scaffolding for old config v6

config migration

implemented MixnetMetricsCleaner

* clippy

* require entry/exit for wireguard

* removed dead code in migration code

* updated config template

* use custom user agent for verloc queries

* fixed premature shutdown of gateway tasks

* hidden nym-api flag to allow illegal node ips

* experiment: final hop handing with wireguard

* added additional startup logs

* typo

* fixed legacy stats endpoint data

* additional logs

* apply review comments

* fixed local testnet manager
2024-12-05 17:21:36 +00:00
Bogdan-Ștefan Neacşu d1f702c4aa Extend raw ws fd for gateway client (#5218) 2024-12-05 14:48:33 +02:00
Tommy Verrall c20c7147f8 Merge pull request #4813 from nymtech/dependabot/npm_and_yarn/testnet-faucet/micromatch-4.0.8
build(deps): bump micromatch from 4.0.4 to 4.0.8 in /testnet-faucet
2024-12-05 10:51:34 +00:00
Tommy Verrall 06956226ad Merge pull request #5195 from nymtech/raphael/update-security
Update Security disclosure email, public key and policy
2024-12-05 10:49:48 +00:00
Jon Häggblad b06091e548 Derive serialize for UserAgent (#5210) 2024-12-05 11:21:33 +01:00
Simon Wicky f3bf5d080b better date serilization (#5207) 2024-12-04 11:11:51 +01:00
dynco-nym e06d442e95 Restore Location fields (#5208)
* Add latitude/longitude fields to Location

* Add regression test

* Bump package version

* Load secret during workflow
2024-12-03 18:35:56 +01:00
dynco-nym fc79f739d4 Fix overflow (#5204) 2024-12-03 10:20:28 +01:00
benedetta davico 6ee8ccbeaa Merge pull request #5199 from nymtech/merge/release/2024.14-crunch
merge crunch into develop
2024-12-02 13:21:04 +01:00
Jędrzej Stuczyński cfebd14655 Merge branch 'release/2024.14-crunch' into merge/release/2024.14-crunch 2024-12-02 11:21:09 +00:00
Simon Wicky 4851614375 NS API - Gateway stats scraping (#5180)
* squashed commit before rebasing

* removed blank lines
2024-12-02 12:15:30 +01:00
benedetta davico ec502f46f0 Merge pull request #5196 from nymtech/release/2024.13-magura-patched-v2
Merging magura drift into crunch
2024-12-02 12:13:12 +01:00
Raphaël Walther 841fb81d24 Update Security disclosure email, public key and policy 2024-11-29 16:54:17 +01:00
dynco-nym a9e62889c3 Remove explorer dependency (#5190)
* Move monitor code to a struct
- to store state in a struct

* explorer deprecation wip

* Replace explorer with ipinfo calls

* PR feedback

* Fix clippy

* Bump package version

* Remove ipinfo crate due to openssl dep

* Add remaining bandwidth log
2024-11-29 16:45:55 +01:00
import this 074d705448 [DOCs/operators]: Magura-drift - second patch (#5194)
* syntax edits

* new version harsh

* changelog info - ready to review
2024-11-29 13:34:58 +00:00
benedettadavico 4a9a5579c4 update changelog 2024-11-29 14:06:32 +01:00
benedetta davico 96180275f8 Update Cargo.toml 2024-11-29 13:57:57 +01:00
Bogdan-Ștefan Neacşu ab20260a2f Guard storage access with cache (#5193)
* Guard storage access with cache

* Do the sync way less freq

* Change sync behaviour for bandwidth too

* Use bigger delta
2024-11-29 14:56:39 +02:00
Tommy Verrall ec1c564c2b Merge pull request #5150 from nymtech/dependabot/npm_and_yarn/testnet-faucet/cross-spawn-7.0.6
build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet
2024-11-29 12:27:29 +00:00
Tommy Verrall bdf97bcbd6 Merge pull request #5151 from nymtech/fix/validator-rewarder-push-docker
fix: validator-rewarder GH job
2024-11-29 12:26:55 +00:00
Jędrzej Stuczyński 889d464e98 improvement: make internal gateway clients use the same topology cache (#5191) 2024-11-29 09:45:12 +00:00
Jędrzej Stuczyński 56206433e6 chore: apply 1.84 linter suggestions (#5192) 2024-11-29 09:20:45 +00:00
Tommy Verrall 8e9d01c47b Merge pull request #5189 from nymtech/fix/network-tunnel-script
Fix/network tunnel script
2024-11-28 15:47:56 +00:00
Tommy Verrall f95f01959c fix multiple forwarding calls
also add more logging around joke section
2024-11-28 12:29:29 +01:00
Tommy Verrall 42de620951 typo 2024-11-28 12:06:03 +01:00
Tommy Verrall af9f7b1c0f formatting 2024-11-28 12:02:45 +01:00
Tommy Verrall 7c1ad7d20c add more output on joke commands
this should help the end users debug their machines further
2024-11-28 12:02:13 +01:00
Tommy Verrall 9ac0595a35 remove duplicate iptable rules 2024-11-28 11:49:29 +01:00
Tommy Verrall c6c138167d Merge pull request #5186 from nymtech/fix/network-tunnel-script
fix for the network tunnel manager script
2024-11-28 09:39:50 +00:00
Tommy Verrall 09633dead1 add the enable ip forwarding method 2024-11-28 10:38:13 +01:00
dynco-nym cd2ad0adbb Update dir in workflow (#5185) 2024-11-27 17:50:55 +01:00
benedetta davico 0b52224917 Update network_tunnel_manager.sh 2024-11-27 17:26:37 +01:00
dynco-nym 96ebe3fc4f Fix overflow (#5184) 2024-11-27 17:07:01 +01:00
Drazen Urch 8e713d43e1 Add monitor_run and testing_route indexes (#5182) 2024-11-27 11:07:39 +01:00
benedettadavico 35aa7e338d bump binary versions 2024-11-26 15:01:06 +01:00
Jędrzej Stuczyński 2a60b2f057 bugfix: fixed nym-node config migrations (again) (#5179) 2024-11-26 09:41:18 +00:00
Jędrzej Stuczyński dcde4c8df1 bugfix: use default value for verloc config when deserialising missing values (#5177) 2024-11-25 17:54:32 +00:00
Tommy Verrall fcaa32284b Merge pull request #5175 from nymtech/fix/empty_allowed
Remove peers with no allowed ip from storage
2024-11-25 17:36:33 +00:00
Bogdan-Ștefan Neacşu fa72f90bfa Remove peers with no allowed ip from storage 2024-11-25 16:44:23 +00:00
Jędrzej Stuczyński 12b9aefa99 bugfix: correctly expose ecash-related data on nym-api (#5155)
* fixed signer related endpoints

* fixed aggregation of partial data if the api is not a signer

* fixes to swagger docs for global ecash endpoints

* remove unused axum_macros

* fixed test traits
2024-11-25 08:39:55 +00:00
dependabot[bot] d2e85f2bfe build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-19 10:32:10 +00:00
Fran Arbanas b28e953a2b fix: validator-rewarder GH job 2024-11-12 17:16:59 +01:00
dependabot[bot] b4ca959800 build(deps): bump micromatch from 4.0.4 to 4.0.8 in /testnet-faucet
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.4 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.4...4.0.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-27 15:46:12 +00:00
302 changed files with 9630 additions and 7904 deletions
+1
View File
@@ -34,6 +34,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
steps: steps:
- name: Install Dependencies (Linux) - name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5 uses: mikefarah/yq@v4.44.6
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml 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" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5 uses: mikefarah/yq@v4.44.6
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml 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" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5 uses: mikefarah/yq@v4.44.6
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
@@ -8,7 +8,7 @@ on:
description: Which gateway probe git ref to build the image with description: Which gateway probe git ref to build the image with
env: env:
WORKING_DIRECTORY: "nym-node-status-agent" WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent"
CONTAINER_NAME: "node-status-agent" CONTAINER_NAME: "node-status-agent"
jobs: jobs:
@@ -31,7 +31,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5 uses: mikefarah/yq@v4.44.6
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -58,4 +58,4 @@ jobs:
- name: BuildAndPushImageOnHarbor - name: BuildAndPushImageOnHarbor
run: | run: |
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }} docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+2 -2
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
WORKING_DIRECTORY: "nym-node-status-api" WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
CONTAINER_NAME: "node-status-api" CONTAINER_NAME: "node-status-api"
jobs: jobs:
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5 uses: mikefarah/yq@v4.44.6
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml 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" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5 uses: mikefarah/yq@v4.44.6
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -0,0 +1,55 @@
name: Build and upload Nyx Chain Watcher container to harbor.nymte.ch
on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "nyx-chain-watcher"
CONTAINER_NAME: "nyx-chain-watcher"
jobs:
build-container:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.nymte.ch
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure git identity
run: |
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.6
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Check if tag exists
run: |
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
fi
- name: Remove existing tag if exists
run: |
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
fi
- name: Create tag
run: |
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
@@ -26,10 +26,10 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5 uses: mikefarah/yq@v4.44.6
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Remove existing tag if exists - name: Remove existing tag if exists
run: | run: |
+4
View File
@@ -4,6 +4,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [2024.13-magura-drift] (2024-11-29)
- Optimised syncing bandwidth information to storage
## [2024.13-magura-patched] (2024-11-22) ## [2024.13-magura-patched] (2024-11-22)
- [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution - [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution
Generated
+152 -130
View File
@@ -11,7 +11,7 @@ dependencies = [
"macroific", "macroific",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -283,7 +283,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -352,7 +352,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -363,7 +363,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -573,7 +573,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -1140,7 +1140,7 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -1790,7 +1790,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -1948,7 +1948,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim 0.11.1", "strsim 0.11.1",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -1970,7 +1970,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
dependencies = [ dependencies = [
"darling_core 0.20.9", "darling_core 0.20.9",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2021,7 +2021,7 @@ dependencies = [
"macroific", "macroific",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2064,7 +2064,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2097,7 +2097,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2165,7 +2165,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2428,7 +2428,7 @@ dependencies = [
[[package]] [[package]]
name = "explorer-api" name = "explorer-api"
version = "1.1.42" version = "1.1.43"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap 4.5.20", "clap 4.5.20",
@@ -2531,7 +2531,7 @@ dependencies = [
"macroific", "macroific",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2765,7 +2765,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2860,7 +2860,7 @@ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -3248,6 +3248,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "human-repr"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1"
[[package]] [[package]]
name = "humantime" name = "humantime"
version = "1.3.0" version = "1.3.0"
@@ -3822,9 +3828,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.162" version = "0.2.167"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
[[package]] [[package]]
name = "libm" name = "libm"
@@ -3950,7 +3956,7 @@ dependencies = [
"cfg-if", "cfg-if",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -3961,7 +3967,7 @@ checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -3974,7 +3980,7 @@ dependencies = [
"macroific_core", "macroific_core",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -4128,6 +4134,21 @@ dependencies = [
"wasm-utils", "wasm-utils",
] ]
[[package]]
name = "mixnet-connectivity-check"
version = "0.1.0"
dependencies = [
"anyhow",
"clap 4.5.20",
"futures",
"nym-bin-common",
"nym-crypto",
"nym-network-defaults",
"nym-sdk",
"tokio",
"tracing",
]
[[package]] [[package]]
name = "moka" name = "moka"
version = "0.12.8" version = "0.12.8"
@@ -4431,7 +4452,7 @@ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -4451,7 +4472,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]] [[package]]
name = "nym-api" name = "nym-api"
version = "1.1.46" version = "1.1.47"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -4701,7 +4722,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-cli" name = "nym-cli"
version = "1.1.44" version = "1.1.45"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.22.1", "base64 0.22.1",
@@ -4784,7 +4805,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-client" name = "nym-client"
version = "1.1.44" version = "1.1.45"
dependencies = [ dependencies = [
"bs58", "bs58",
"clap 4.5.20", "clap 4.5.20",
@@ -4851,6 +4872,7 @@ dependencies = [
"nym-gateway-requests", "nym-gateway-requests",
"nym-id", "nym-id",
"nym-metrics", "nym-metrics",
"nym-mixnet-client",
"nym-network-defaults", "nym-network-defaults",
"nym-nonexhaustive-delayqueue", "nym-nonexhaustive-delayqueue",
"nym-pemstore", "nym-pemstore",
@@ -5443,7 +5465,7 @@ dependencies = [
"nym-mixnode-common", "nym-mixnode-common",
"nym-network-defaults", "nym-network-defaults",
"nym-network-requester", "nym-network-requester",
"nym-node-http-api", "nym-node-metrics",
"nym-sdk", "nym-sdk",
"nym-sphinx", "nym-sphinx",
"nym-statistics-common", "nym-statistics-common",
@@ -5535,8 +5557,11 @@ name = "nym-gateway-stats-storage"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"nym-credentials-interface", "nym-credentials-interface",
"nym-node-metrics",
"nym-sphinx", "nym-sphinx",
"nym-statistics-common",
"sqlx", "sqlx",
"strum 0.26.3",
"thiserror", "thiserror",
"time", "time",
"tokio", "tokio",
@@ -5547,7 +5572,6 @@ dependencies = [
name = "nym-gateway-storage" name = "nym-gateway-storage"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait",
"bincode", "bincode",
"defguard_wireguard_rs", "defguard_wireguard_rs",
"log", "log",
@@ -5740,11 +5764,11 @@ name = "nym-mixnet-client"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures", "futures",
"log",
"nym-sphinx", "nym-sphinx",
"nym-task", "nym-task",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing",
] ]
[[package]] [[package]]
@@ -5771,35 +5795,6 @@ dependencies = [
"utoipa", "utoipa",
] ]
[[package]]
name = "nym-mixnode"
version = "1.1.37"
dependencies = [
"colored",
"futures",
"nym-contracts-common",
"nym-crypto",
"nym-http-api-common",
"nym-metrics",
"nym-mixnet-client",
"nym-mixnode-common",
"nym-node-http-api",
"nym-nonexhaustive-delayqueue",
"nym-sphinx",
"nym-sphinx-params",
"nym-sphinx-types",
"nym-task",
"nym-topology",
"nym-types",
"nym-validator-client",
"thiserror",
"time",
"tokio",
"tokio-util",
"tracing",
"url",
]
[[package]] [[package]]
name = "nym-mixnode-common" name = "nym-mixnode-common"
version = "0.1.0" version = "0.1.0"
@@ -5808,11 +5803,9 @@ dependencies = [
"futures", "futures",
"humantime-serde", "humantime-serde",
"log", "log",
"nym-bin-common",
"nym-crypto", "nym-crypto",
"nym-metrics", "nym-metrics",
"nym-network-defaults", "nym-network-defaults",
"nym-node-http-api",
"nym-sphinx-acknowledgements", "nym-sphinx-acknowledgements",
"nym-sphinx-addressing", "nym-sphinx-addressing",
"nym-sphinx-forwarding", "nym-sphinx-forwarding",
@@ -5820,7 +5813,6 @@ dependencies = [
"nym-sphinx-params", "nym-sphinx-params",
"nym-sphinx-types", "nym-sphinx-types",
"nym-task", "nym-task",
"nym-validator-client",
"rand", "rand",
"serde", "serde",
"thiserror", "thiserror",
@@ -5870,6 +5862,7 @@ dependencies = [
"futures", "futures",
"log", "log",
"nym-bin-common", "nym-bin-common",
"nym-client-core",
"nym-crypto", "nym-crypto",
"nym-network-defaults", "nym-network-defaults",
"nym-sdk", "nym-sdk",
@@ -5891,7 +5884,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-network-requester" name = "nym-network-requester"
version = "1.1.45" version = "1.1.46"
dependencies = [ dependencies = [
"addr", "addr",
"anyhow", "anyhow",
@@ -5942,9 +5935,12 @@ dependencies = [
[[package]] [[package]]
name = "nym-node" name = "nym-node"
version = "1.1.11" version = "1.1.12"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait",
"axum 0.7.7",
"axum-extra",
"bip39", "bip39",
"bs58", "bs58",
"cargo_metadata 0.18.1", "cargo_metadata 0.18.1",
@@ -5952,6 +5948,10 @@ dependencies = [
"clap 4.5.20", "clap 4.5.20",
"colored", "colored",
"cupid", "cupid",
"dashmap",
"futures",
"headers",
"human-repr",
"humantime-serde", "humantime-serde",
"ipnetwork 0.20.0", "ipnetwork 0.20.0",
"nym-authenticator", "nym-authenticator",
@@ -5960,63 +5960,60 @@ dependencies = [
"nym-config", "nym-config",
"nym-crypto", "nym-crypto",
"nym-gateway", "nym-gateway",
"nym-gateway-stats-storage",
"nym-http-api-common",
"nym-ip-packet-router", "nym-ip-packet-router",
"nym-mixnode", "nym-metrics",
"nym-mixnet-client",
"nym-network-requester", "nym-network-requester",
"nym-node-http-api", "nym-node-metrics",
"nym-node-requests",
"nym-nonexhaustive-delayqueue",
"nym-pemstore", "nym-pemstore",
"nym-sphinx-acknowledgements", "nym-sphinx-acknowledgements",
"nym-sphinx-addressing", "nym-sphinx-addressing",
"nym-sphinx-forwarding",
"nym-sphinx-framing",
"nym-sphinx-types",
"nym-task", "nym-task",
"nym-topology",
"nym-types", "nym-types",
"nym-validator-client", "nym-validator-client",
"nym-verloc",
"nym-wireguard", "nym-wireguard",
"nym-wireguard-types", "nym-wireguard-types",
"rand", "rand",
"semver 1.0.23", "semver 1.0.23",
"serde", "serde",
"serde_json", "serde_json",
"si-scale",
"sysinfo", "sysinfo",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"tokio-util",
"toml 0.8.14", "toml 0.8.14",
"tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"url", "url",
"utoipa",
"utoipa-swagger-ui",
"zeroize", "zeroize",
] ]
[[package]] [[package]]
name = "nym-node-http-api" name = "nym-node-metrics"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"axum 0.7.7",
"axum-extra",
"base64 0.22.1",
"colored",
"dashmap", "dashmap",
"fastrand 2.1.1", "futures",
"headers",
"hmac",
"hyper 1.4.1",
"ipnetwork 0.20.0",
"nym-crypto",
"nym-http-api-common",
"nym-metrics", "nym-metrics",
"nym-node-requests", "nym-statistics-common",
"nym-task", "strum 0.26.3",
"nym-wireguard",
"rand",
"serde_json",
"thiserror",
"time", "time",
"tokio", "tokio",
"tower 0.4.13",
"tower-http",
"tracing", "tracing",
"utoipa",
"utoipa-swagger-ui",
"x25519-dalek",
] ]
[[package]] [[package]]
@@ -6061,7 +6058,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-node-status-api" name = "nym-node-status-api"
version = "1.0.0-rc.2" version = "1.0.0-rc.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum 0.7.7", "axum 0.7.7",
@@ -6075,8 +6072,11 @@ dependencies = [
"nym-crypto", "nym-crypto",
"nym-explorer-client", "nym-explorer-client",
"nym-network-defaults", "nym-network-defaults",
"nym-node-metrics",
"nym-node-requests", "nym-node-requests",
"nym-node-status-client", "nym-node-status-client",
"nym-serde-helpers",
"nym-statistics-common",
"nym-task", "nym-task",
"nym-validator-client", "nym-validator-client",
"regex", "regex",
@@ -6088,6 +6088,7 @@ dependencies = [
"strum 0.26.3", "strum 0.26.3",
"strum_macros 0.26.4", "strum_macros 0.26.4",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tower-http", "tower-http",
@@ -6306,7 +6307,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.44" version = "1.1.45"
dependencies = [ dependencies = [
"bs58", "bs58",
"clap 4.5.20", "clap 4.5.20",
@@ -6808,6 +6809,25 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "nym-verloc"
version = "0.1.0"
dependencies = [
"bytes",
"futures",
"humantime 2.1.0",
"nym-crypto",
"nym-task",
"nym-validator-client",
"rand",
"thiserror",
"time",
"tokio",
"tokio-util",
"tracing",
"url",
]
[[package]] [[package]]
name = "nym-vesting-contract-common" name = "nym-vesting-contract-common"
version = "0.7.0" version = "0.7.0"
@@ -6886,8 +6906,10 @@ dependencies = [
"nym-task", "nym-task",
"nym-wireguard-types", "nym-wireguard-types",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tracing",
"x25519-dalek", "x25519-dalek",
] ]
@@ -6908,7 +6930,7 @@ dependencies = [
[[package]] [[package]]
name = "nymvisor" name = "nymvisor"
version = "0.1.9" version = "0.1.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -7221,7 +7243,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -7308,7 +7330,7 @@ dependencies = [
"pest_meta", "pest_meta",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -7349,7 +7371,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -7564,14 +7586,14 @@ dependencies = [
"proc-macro-error-attr2", "proc-macro-error-attr2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.89" version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -7584,7 +7606,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
"version_check", "version_check",
"yansi", "yansi",
] ]
@@ -7647,7 +7669,7 @@ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -7880,7 +7902,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8107,7 +8129,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rocket_http", "rocket_http",
"syn 2.0.87", "syn 2.0.90",
"unicode-xid", "unicode-xid",
"version_check", "version_check",
] ]
@@ -8233,7 +8255,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rust-embed-utils", "rust-embed-utils",
"syn 2.0.87", "syn 2.0.90",
"walkdir", "walkdir",
] ]
@@ -8289,9 +8311,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.40" version = "0.38.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"errno", "errno",
@@ -8482,7 +8504,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals 0.29.1", "serde_derive_internals 0.29.1",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8514,7 +8536,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8646,7 +8668,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8657,7 +8679,7 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8668,7 +8690,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8730,7 +8752,7 @@ checksum = "aafbefbe175fa9bf03ca83ef89beecff7d2a95aaacd5732325b90ac8c3bd7b90"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8751,7 +8773,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -8802,7 +8824,7 @@ dependencies = [
"darling 0.20.9", "darling 0.20.9",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -9401,7 +9423,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -9444,9 +9466,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.87" version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -9752,7 +9774,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -9883,7 +9905,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -10162,7 +10184,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -10326,7 +10348,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
"termcolor", "termcolor",
] ]
@@ -10353,7 +10375,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals 0.28.0", "serde_derive_internals 0.28.0",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -10556,7 +10578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c" checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -10588,7 +10610,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn 2.0.87", "syn 2.0.90",
"toml 0.5.11", "toml 0.5.11",
"uniffi_build", "uniffi_build",
"uniffi_meta", "uniffi_meta",
@@ -10720,7 +10742,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
"syn 2.0.87", "syn 2.0.90",
"uuid", "uuid",
] ]
@@ -10759,7 +10781,7 @@ checksum = "17e82ab96c5a55263b5bed151b8426410d93aa909a453acdbd4b6792b5af7d64"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -10770,7 +10792,7 @@ checksum = "86b8338dc3c9526011ffaa2aa6bd60ddfda9d49d2123108690755c6e34844212"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
"utoipauto-core", "utoipauto-core",
] ]
@@ -10877,7 +10899,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@@ -10911,7 +10933,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@@ -10945,7 +10967,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -11473,7 +11495,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -11493,7 +11515,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.87", "syn 2.0.90",
] ]
[[package]] [[package]]
+3 -5
View File
@@ -104,7 +104,6 @@ members = [
"explorer-api/explorer-client", "explorer-api/explorer-client",
"gateway", "gateway",
"integrations/bity", "integrations/bity",
"mixnode",
"sdk/ffi/cpp", "sdk/ffi/cpp",
"sdk/ffi/go", "sdk/ffi/go",
"sdk/ffi/shared", "sdk/ffi/shared",
@@ -123,8 +122,8 @@ members = [
"nym-data-observatory", "nym-data-observatory",
"nym-network-monitor", "nym-network-monitor",
"nym-node", "nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests", "nym-node/nym-node-requests",
"nym-node/nym-node-metrics",
"nym-node-status-api/nym-node-status-agent", "nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api", "nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client", "nym-node-status-api/nym-node-status-client",
@@ -149,15 +148,13 @@ members = [
"tools/internal/contract-state-importer/importer-cli", "tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract", "tools/internal/contract-state-importer/importer-contract",
"tools/internal/testnet-manager", "tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract", "tools/internal/testnet-manager/dkg-bypass-contract", "common/verloc", "tools/internal/mixnet-connectivity-check",
] ]
default-members = [ default-members = [
"clients/native", "clients/native",
"clients/socks5", "clients/socks5",
"explorer-api", "explorer-api",
"gateway",
"mixnode",
"nym-api", "nym-api",
"nym-credential-proxy/nym-credential-proxy", "nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory", "nym-data-observatory",
@@ -263,6 +260,7 @@ http-body-util = "0.1"
httpcodec = "0.2.3" httpcodec = "0.2.3"
humantime = "2.1.0" humantime = "2.1.0"
humantime-serde = "1.1.1" humantime-serde = "1.1.1"
human-repr = "1.1.0"
hyper = "1.4.1" hyper = "1.4.1"
hyper-util = "0.1" hyper-util = "0.1"
indicatif = "0.17.8" indicatif = "0.17.8"
+68 -56
View File
@@ -3,37 +3,23 @@ Critical bug or security issue 💥
If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to: If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to:
``` ```
security@nymte.ch security@nym.com
``` ```
Encrypted with our public key which is available below in plain text and also on keyservers: Encrypted with our public key which is available below in plain text and also on keyservers:
``` ```
pub rsa4096 2023-10-30 [SC] [expire : 2026-10-29] sec rsa4096/7C3C727F05090550 2023-10-30 [SC] [expire : 2026-10-29]
24B2592E801A5AAA8666C8BA7C3C727F05090550 24B2592E801A5AAA8666C8BA7C3C727F05090550
uid [ ultime ] Security Nym Technologies <security@nymte.ch> uid [ ultime ] Security Nym Technologies <security@nym.com>
sub rsa4096 2023-10-30 [E] [expire : 2026-10-29] ssb rsa4096/ACD0FBD79DC70ACC 2023-10-30 [E] [expire : 2026-10-29]
``` ```
The fingerprint of the key is on the second line above. The fingerprint of the key is on the second line above.
If you need to chat __urgently__ to our team for a __critical__ security issue:
go to Matrix, and alert the core engineers with a private direct message:
Jedrzej Stuczynski @jstuczyn:nymtech.chat
Mark Sinclair @mark:nymtech.chat
Raphaël Walther @raphael:nymtech.chat
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues. Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
If you don't know what Matrix is, you can follow this documentation to create an account on this federation of instant messaging servers:
[Matrix for Instant Messaging](https://matrix.org/docs/chat_basics/matrix-for-im/)
``` ```
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -48,43 +34,69 @@ vMFUIzBMHOPXH16036zGyFMC1esRd2qqil4b9KtLgCOkrD1VgpjcveoA0VyMJCN6
LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A
fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj
IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB
tC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW10ZS5jaD6J tCxTZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW0uY29tPokC
AlQEEwEKAD4WIQQkslkugBpaqoZmyLp8PHJ/BQkFUAUCZT9elwIbAwUJBaOagAUL VAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJnSd5VAhsDBQkFo5qABQsJ
CQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB8PHJ/BQkFUL7dD/9zO73uI5VR+SWx CAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQPPIP/ipGz2zLAjE2dSE3
PFmJW+9QsPiQbVRvGwNZurctmQ2s2Pe0vHRELFeqD5oYvSx2Lequ3Ir+zn/C3kDM VcqOvras0DfqIL9HDm26Dg6QO2D/4YRntw0RqVyuy+zFnRUm+RZCKLPLUzbQ9Wjb
kNs40obSL6jCBiLPkxEY0JqzPM9jZr7EjvlibWV3f6DxooRIqEyfN57I3OBGlqZE G/Og5ttQVYQMu5eKu7OMvXkrbRo3teZFU+8IL08zIW6pyf9haxO6YMhLRy6cLYwW
0Mx7sQuCcgau8C70DF952QhKUwXC2cmpmDKHVEEoio1xGSD4dQhGapCB32RQGtna 0EYC6Qzn5gz3kI7VkI8fWfs2Dk4XEV3D+SVtBoF6KRxMXT6HZvpzoMSEJZBoNj8S
OGfAO9celNMvSq0Lp+aJxeACmWFY5T4/y79JPcT5vSs/yEIRmaH/fn2piwaFBsIq jw0TF8TFUQf49jUQbIHumukMswolrHi8a5ej8DSfNwSgz+Tt8oh5lu01kyUJiHn7
gHJJMxO3740P1hF8j7KWUoUofuFaEALHBpEpjWTOj8ej1wmFlu+5F+jSVoc781Wb nuHaY4Y9cHUVAOSwq/hovG52+ZE1r3aiswvle/B19o9pKeWWVvacSptGxDQagBtQ
ZZXu04cOBXnGTogzSxMpBe9TtLb28zd6WzFotC25KTI3pngMzXsQGLJLOwvoZKiS igoNLdRvY0XN2TEyX9pOHR0AoVOxtIW11CpkKuDbQG9vPwovqJ2L6+Fh3pzHYzcI
LFjPRjg1rwobmB3Q3J2W5GYSveia0CDsZGP+g87GVVf/oD2Djpa68xyVYwIYeA6T 2GIShNm/Z2SZBiUqbljJe9H4UAT/aHgMINkEG8qzUKwO42MA5HJT7YbHTR17/QSF
3DNdS77qHiRuGiS4kWXyVjDqOICboR4uCvt09zlkBuLDdTWqWYARUvZjtjs4w/Ol Il5dhneRzmSbNcW2rdRwx/BmzrcsFJfqCt4JG/WDF293xSOjhFqQYvU4gCO+OB7o
rdrBI3A88ti8fRldYaNpu17ME1ilpN44yKoJtqiWc3Tisk8eYLfx6c7FQF3PrRva KXjX907XXDjS2KEJ71OGqVfk/P7BqEfQNfrLtb02TyXJAPQXHhybv23c4E7zUs9V
mr7FZvhFsYML5CeNFHTEzN6Y3jjKN/60DvCfodWnWFK47Txkl8UAXGY2W9B0fWqQ lMjNizzxYB96uwJb0LAB2ijzEwoP91uGT2tFjk6F08x2QiArmXUdgrv44b39Stia
wUVr8uLuMyyMiKbeoufi7rGOj6AMErkCDQRlP16XARAA8FGmD5J3tM1BOM1niJxZ gJS0GYKqSzyr10xHhUuDA+GKYtcitC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVz
JTdCauzEtxEoBL0RuqGBkR8U29sRM6DwuzjU7PwscFnBaGyU+eU73GwGkH3ozFfF IDxzZWN1cml0eUBueW10ZS5jaD6JAjYEMAEKACAWIQQkslkugBpaqoZmyLp8PHJ/
tllYhQrhP/kkN+0rEO5Xi+nR+4JCFRqrf3nJXAAPfiksURMp8er1dUOY2/e1ZSoL BQkFUAUCZ0nftQIdIAAKCRB8PHJ/BQkFUFHDEACtyNuUEjKCLAT5mSfow85PjFgo
tS+nzUivV8CfE+pgj/5YtGwPC+KYHLATkKkMELCrbW4UO06VWOqQsvr6kivXuJQQ o8kHjQr/IIQ7ZbBOHeJJcrxDuypssiLh5XUjF3x5BiBfZ6vCxSb81RRwsDMp0mA1
LdEAMpBlADmXFG45DmPKQzsBWUgvTwyGy3LX0nys8cgpex9BH8hhr01QmGyP469s qzv9G8sgW0HTQUnZ9oH6CYut2NgzAnQpmuacrunm9Zy0FJ3ejbmwUY/NqK6gJkle
N3cNrtFuu8U6RAsiCD/8mlBuD3EQEU5SF0lc7kCICAZk+wElmXnimEi0TOYsbz6k 66duHKhAy7DWjj7amd0C8bPDR+PA44fI3MezDHkQNaauKZTRqd1TqH8Qk5PAl4cB
90lteicX70rA9GNeyI76H+VSOYvWpkRwaJAgUdzrAM1o9SHASq+cZ6nD85OZioQk o5gVzeZh/U7/usvtGhazAIUF5BqK6bTmDnYopg+2x8jjwrG4+08GrttZkNjBLXeA
DWM6+Q+sf2oen0qJnnGmUr93kJIC0PIdgrXRrtiNfeRa1Z/H0LmREyyEMoFiVivn Y/2U064yMz12LPv01qqAFdZ+coRy/ps/gOQTz34/VeW0CFy7TMqs4t3vSBWTqU7w
z1vVk85Oq6Sf3ltUwvmDzuuJOtsp2Qp6+x6Snn/yKauI4uf4Cf/wKUch4r6Bwgg5 hnw/qj6cM33fdxctj6KDgJSCkZdx2fvwXgxiPqUa5+j9FlFBeD5RDAl6g6t8N1/K
Dw49ky7lwlnALio4GIVoGLpLef93wWoDmp4Klyh3ZPf2nB0U91u3bHRUo7m+D7QJ Xca+zNYuSZgc297q1D+mtSD1C7uJNPxoAl+Bv5KNKpsjfQ+m04++CIFtGyX22aCA
98cyKtqLLzjg7szGf60pIWNWRsadYQT3bSncynqknAjOV3BCvx6/ivsnpj//QjYR h2/tHwQZIXhOiMAKOoupidDVDhgxtCJ3Ps416xL0sTZfsPfg+j1Uv/Em9pzPClEl
HtviUAcQ1DBB6UC6q23FIs0AEQEAAYkCPAQYAQoAJhYhBCSyWS6AGlqqhmbIunw8 fX6+1O4DdSyZUQ4VsjMu/H5W/NQdbHgmqFrxQ6WX/0s5GMwO6GMDiPe8sOrwz9wD
cn8FCQVQBQJlP16XAhsMBQkFo5qAAAoJEHw8cn8FCQVQzukP/iLxjOxT+UpPR//c WYtyjafxXOHEZ1OjYX5gr7bGaG4oKc2btTJN0B3Phg4dStnHCNjEYccxuV3507fj
prDVSLkP4pF5bmw36U07jvqpS+/KTXsxiiQleffRabOpNLcd+K1ueavyt9nnIwHH HnNotkpXF2nGLxy+PYkCVAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJl
tHS9kM9A7DBw3LnpEbXki46QDCCI6niGijlLOEeAWqnocwMNTT05wVVgCtO3DQP2 P16XAhsDBQkFo5qABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQ
MoSCcqHpXDChvOyr5d5xjYLVJhlctIMSomcVzGryjknPu0Yj/TkC/4c+m86ZWQUD vt0P/3M7ve4jlVH5JbE8WYlb71Cw+JBtVG8bA1m6ty2ZDazY97S8dEQsV6oPmhi9
HqMHQIuiEenvb62/F4c5OJIRZPEn70wdddkgJuJU3eHdHrnuhCkjCC93GQGbGj03 LHYt6q7civ7Of8LeQMyQ2zjShtIvqMIGIs+TERjQmrM8z2NmvsSO+WJtZXd/oPGi
Zqos6699y6hmPeD3U5IUv8ujwZYVCCuDm8gJfrp3R6WLfeZeK9WmTVBpCzsDg3fV hEioTJ83nsjc4EaWpkTQzHuxC4JyBq7wLvQMX3nZCEpTBcLZyamYModUQSiKjXEZ
hSwmOk6pp8DAq1/Dev3yRkFggCEyGK6c9b+a0CRBncl8e5Q0QQIzNiS/uExQP3h+ IPh1CEZqkIHfZFAa2do4Z8A71x6U0y9KrQun5onF4AKZYVjlPj/Lv0k9xPm9Kz/I
ELJs3P0MLP+6FWhNUry09n3lnWkr1hY+v1M0GAxbfdv/tsCN1Pq/VQEz+CTqXqya QhGZof9+famLBoUGwiqAckkzE7fvjQ/WEXyPspZShSh+4VoQAscGkSmNZM6Px6PX
ftWldOHWw6Hh+gtwxcHjG4MBOrO5oICQ3lh2hGwQ58cDgZYSK/OGgJ9BggFl1CcM CYWW77kX6NJWhzvzVZtlle7Thw4FecZOiDNLEykF71O0tvbzN3pbMWi0LbkpMjem
0uGC0/TRCI1zt/4y+7efSZQMZkHo7VC/3MFbp2hcNejpW+BxVuwKTunFvWK3TLhq eAzNexAYsks7C+hkqJIsWM9GODWvChuYHdDcnZbkZhK96JrQIOxkY/6DzsZVV/+g
sSlQ5yyhqchooepsFHq9bosKFjLJC01uprBv1rinoNduOy43FbyS7JPRRspANN0R PYOOlrrzHJVjAhh4DpPcM11LvuoeJG4aJLiRZfJWMOo4gJuhHi4K+3T3OWQG4sN1
iC2pMbWdE0ZTQaFq6tPIg058pjqi NapZgBFS9mO2OzjD86Wt2sEjcDzy2Lx9GV1ho2m7XswTWKWk3jjIqgm2qJZzdOKy
=nqgX Tx5gt/HpzsVAXc+tG9qavsVm+EWxgwvkJ40UdMTM3pjeOMo3/rQO8J+h1adYUrjt
PGSXxQBcZjZb0HR9apDBRWvy4u4zLIyIpt6i5+LusY6PoAwSuQINBGU/XpcBEADw
UaYPkne0zUE4zWeInFklN0Jq7MS3ESgEvRG6oYGRHxTb2xEzoPC7ONTs/CxwWcFo
bJT55TvcbAaQfejMV8W2WViFCuE/+SQ37SsQ7leL6dH7gkIVGqt/eclcAA9+KSxR
Eynx6vV1Q5jb97VlKgu1L6fNSK9XwJ8T6mCP/li0bA8L4pgcsBOQqQwQsKttbhQ7
TpVY6pCy+vqSK9e4lBAt0QAykGUAOZcUbjkOY8pDOwFZSC9PDIbLctfSfKzxyCl7
H0EfyGGvTVCYbI/jr2w3dw2u0W67xTpECyIIP/yaUG4PcRARTlIXSVzuQIgIBmT7
ASWZeeKYSLRM5ixvPqT3SW16JxfvSsD0Y17Ijvof5VI5i9amRHBokCBR3OsAzWj1
IcBKr5xnqcPzk5mKhCQNYzr5D6x/ah6fSomecaZSv3eQkgLQ8h2CtdGu2I195FrV
n8fQuZETLIQygWJWK+fPW9WTzk6rpJ/eW1TC+YPO64k62ynZCnr7HpKef/Ipq4ji
5/gJ//ApRyHivoHCCDkPDj2TLuXCWcAuKjgYhWgYukt5/3fBagOangqXKHdk9/ac
HRT3W7dsdFSjub4PtAn3xzIq2osvOODuzMZ/rSkhY1ZGxp1hBPdtKdzKeqScCM5X
cEK/Hr+K+yemP/9CNhEe2+JQBxDUMEHpQLqrbcUizQARAQABiQI8BBgBCgAmFiEE
JLJZLoAaWqqGZsi6fDxyfwUJBVAFAmU/XpcCGwwFCQWjmoAACgkQfDxyfwUJBVDO
6Q/+IvGM7FP5Sk9H/9ymsNVIuQ/ikXlubDfpTTuO+qlL78pNezGKJCV599Fps6k0
tx34rW55q/K32ecjAce0dL2Qz0DsMHDcuekRteSLjpAMIIjqeIaKOUs4R4Baqehz
Aw1NPTnBVWAK07cNA/YyhIJyoelcMKG87Kvl3nGNgtUmGVy0gxKiZxXMavKOSc+7
RiP9OQL/hz6bzplZBQMeowdAi6IR6e9vrb8Xhzk4khFk8SfvTB112SAm4lTd4d0e
ue6EKSMIL3cZAZsaPTdmqizrr33LqGY94PdTkhS/y6PBlhUIK4ObyAl+undHpYt9
5l4r1aZNUGkLOwODd9WFLCY6TqmnwMCrX8N6/fJGQWCAITIYrpz1v5rQJEGdyXx7
lDRBAjM2JL+4TFA/eH4Qsmzc/Qws/7oVaE1SvLT2feWdaSvWFj6/UzQYDFt92/+2
wI3U+r9VATP4JOperJp+1aV04dbDoeH6C3DFweMbgwE6s7mggJDeWHaEbBDnxwOB
lhIr84aAn0GCAWXUJwzS4YLT9NEIjXO3/jL7t59JlAxmQejtUL/cwVunaFw16Olb
4HFW7ApO6cW9YrdMuGqxKVDnLKGpyGih6mwUer1uiwoWMskLTW6msG/WuKeg1247
LjcVvJLsk9FGykA03RGILakxtZ0TRlNBoWrq08iDTnymOqI=
=QPTf
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.44" version = "1.1.45"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.44" version = "1.1.45"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] 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" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
+1
View File
@@ -46,6 +46,7 @@ nym-sphinx = { path = "../nymsphinx" }
nym-statistics-common = { path = "../statistics" } nym-statistics-common = { path = "../statistics" }
nym-pemstore = { path = "../pemstore" } nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["serializable"] } nym-topology = { path = "../topology", features = ["serializable"] }
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" } nym-task = { path = "../task" }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
@@ -14,7 +14,7 @@ use std::os::raw::c_int as RawFd;
use thiserror::Error; use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use futures::channel::{mpsc, oneshot}; use futures::channel::oneshot;
// we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch // we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -170,7 +170,7 @@ pub struct LocalGateway {
// 'sender' part // 'sender' part
/// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers. /// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers.
packet_forwarder: mpsc::UnboundedSender<MixPacket>, packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender,
// 'receiver' part // 'receiver' part
packet_router_tx: Option<oneshot::Sender<PacketRouter>>, packet_router_tx: Option<oneshot::Sender<PacketRouter>>,
@@ -180,7 +180,7 @@ pub struct LocalGateway {
impl LocalGateway { impl LocalGateway {
pub fn new( pub fn new(
local_identity: identity::PublicKey, local_identity: identity::PublicKey,
packet_forwarder: mpsc::UnboundedSender<MixPacket>, packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender,
packet_router_tx: oneshot::Sender<PacketRouter>, packet_router_tx: oneshot::Sender<PacketRouter>,
) -> Self { ) -> Self {
LocalGateway { LocalGateway {
@@ -208,8 +208,7 @@ mod nonwasm_sealed {
impl GatewaySender for LocalGateway { impl GatewaySender for LocalGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> { async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.packet_forwarder self.packet_forwarder
.unbounded_send(packet) .forward_packet(packet)
.map_err(|err| err.into_send_error())
.map_err(erase_err) .map_err(erase_err)
} }
} }
@@ -38,7 +38,7 @@ pub struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, Option<NymTopology>>, permit: RwLockReadGuard<'a, Option<NymTopology>>,
} }
impl<'a> Deref for TopologyReadPermit<'a> { impl Deref for TopologyReadPermit<'_> {
type Target = Option<NymTopology>; type Target = Option<NymTopology>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@@ -46,7 +46,8 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)] #[cfg(unix)]
match _conn.get_ref() { match _conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()), MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
&_ => None, MaybeTlsStream::Rustls(tls_stream) => Some(tls_stream.as_raw_fd()),
_ => None,
} }
#[cfg(not(unix))] #[cfg(not(unix))]
None None
+8 -4
View File
@@ -9,10 +9,14 @@ license.workspace = true
[dependencies] [dependencies]
futures = { workspace = true } futures = { workspace = true }
log = { workspace = true } tracing = { workspace = true }
tokio = { workspace = true, features = ["time", "net", "rt"] } tokio = { workspace = true, features = ["time"] }
tokio-util = { workspace = true, features = ["codec"] } tokio-util = { workspace = true, features = ["codec"], optional = true }
# internal # internal
nym-sphinx = { path = "../../nymsphinx" } nym-sphinx = { path = "../../nymsphinx" }
nym-task = { path = "../../task" } nym-task = { path = "../../task", optional = true }
[features]
default = ["client"]
client = ["tokio-util", "nym-task", "tokio/net", "tokio/rt"]
@@ -3,7 +3,6 @@
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use log::*;
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress; use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx::framing::codec::NymCodec; use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket; use nym_sphinx::framing::packet::FramedNymPacket;
@@ -18,13 +17,14 @@ use std::time::Duration;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::time::sleep; use tokio::time::sleep;
use tokio_util::codec::Framed; use tokio_util::codec::Framed;
use tracing::*;
#[derive(Clone, Copy)]
pub struct Config { pub struct Config {
initial_reconnection_backoff: Duration, initial_reconnection_backoff: Duration,
maximum_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration, initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize, maximum_connection_buffer_size: usize,
use_legacy_version: bool,
} }
impl Config { impl Config {
@@ -33,14 +33,12 @@ impl Config {
maximum_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration, initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize, maximum_connection_buffer_size: usize,
use_legacy_version: bool,
) -> Self { ) -> Self {
Config { Config {
initial_reconnection_backoff, initial_reconnection_backoff,
maximum_reconnection_backoff, maximum_reconnection_backoff,
initial_connection_timeout, initial_connection_timeout,
maximum_connection_buffer_size, maximum_connection_buffer_size,
use_legacy_version,
} }
} }
} }
@@ -200,9 +198,8 @@ impl SendWithoutResponse for Client {
packet: NymPacket, packet: NymPacket,
packet_type: PacketType, packet_type: PacketType,
) -> io::Result<()> { ) -> io::Result<()> {
trace!("Sending packet to {:?}", address); trace!("Sending packet to {address:?}");
let framed_packet = let framed_packet = FramedNymPacket::new(packet, packet_type);
FramedNymPacket::new(packet, packet_type, self.config.use_legacy_version);
if let Some(sender) = self.conn_new.get_mut(&address) { if let Some(sender) = self.conn_new.get_mut(&address) {
if let Err(err) = sender.channel.try_send(framed_packet) { if let Err(err) = sender.channel.try_send(framed_packet) {
@@ -260,7 +257,6 @@ mod tests {
maximum_reconnection_backoff: Duration::from_millis(300_000), maximum_reconnection_backoff: Duration::from_millis(300_000),
initial_connection_timeout: Duration::from_millis(1_500), initial_connection_timeout: Duration::from_millis(1_500),
maximum_connection_buffer_size: 128, maximum_connection_buffer_size: 128,
use_legacy_version: false,
}) })
} }
@@ -1,77 +1,72 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::{Client, Config, SendWithoutResponse};
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::channel::mpsc::SendError;
use log::*;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use std::time::Duration; use tokio::time::Instant;
pub type MixForwardingSender = mpsc::UnboundedSender<MixPacket>; pub fn mix_forwarding_channels() -> (MixForwardingSender, MixForwardingReceiver) {
type MixForwardingReceiver = mpsc::UnboundedReceiver<MixPacket>; let (tx, rx) = mpsc::unbounded();
(tx.into(), rx)
/// A specialisation of client such that it forwards any received packets on the channel into the
/// mix network immediately, i.e. will not try to listen for any responses.
pub struct PacketForwarder {
mixnet_client: Client,
packet_receiver: MixForwardingReceiver,
shutdown: nym_task::TaskClient,
} }
impl PacketForwarder { #[derive(Clone)]
pub fn new( pub struct MixForwardingSender(mpsc::UnboundedSender<PacketToForward>);
initial_reconnection_backoff: Duration,
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
shutdown: nym_task::TaskClient,
) -> (PacketForwarder, MixForwardingSender) {
let client_config = Config::new(
initial_reconnection_backoff,
maximum_reconnection_backoff,
initial_connection_timeout,
maximum_connection_buffer_size,
use_legacy_version,
);
let (packet_sender, packet_receiver) = mpsc::unbounded(); impl From<mpsc::UnboundedSender<PacketToForward>> for MixForwardingSender {
fn from(tx: mpsc::UnboundedSender<PacketToForward>) -> Self {
MixForwardingSender(tx)
}
}
( impl MixForwardingSender {
PacketForwarder { pub fn forward_packet(&self, packet: impl Into<PacketToForward>) -> Result<(), SendError> {
mixnet_client: Client::new(client_config), self.0
packet_receiver, .unbounded_send(packet.into())
shutdown, .map_err(|err| err.into_send_error())
},
packet_sender,
)
} }
pub async fn run(&mut self) { #[allow(clippy::len_without_is_empty)]
while !self.shutdown.is_shutdown() { pub fn len(&self) -> usize {
tokio::select! { self.0.len()
biased; }
_ = self.shutdown.recv() => { }
log::trace!("PacketForwarder: Received shutdown");
}
Some(mix_packet) = self.packet_receiver.next() => {
trace!("Going to forward packet to {}", mix_packet.next_hop());
let next_hop = mix_packet.next_hop(); pub type MixForwardingReceiver = mpsc::UnboundedReceiver<PacketToForward>;
let packet_type = mix_packet.packet_type();
let packet = mix_packet.into_packet();
// we don't care about responses, we just want to fire packets
// as quickly as possible
if let Err(err) = pub struct PacketToForward {
self.mixnet_client pub packet: MixPacket,
.send_without_response(next_hop, packet, packet_type) pub forward_delay_target: Option<Instant>,
{ }
debug!("failed to forward the packet - {err}")
} impl From<MixPacket> for PacketToForward {
} fn from(packet: MixPacket) -> Self {
} PacketToForward::new_no_delay(packet)
}
}
impl From<(MixPacket, Option<Instant>)> for PacketToForward {
fn from((packet, delay_until): (MixPacket, Option<Instant>)) -> Self {
PacketToForward::new(packet, delay_until)
}
}
impl From<(MixPacket, Instant)> for PacketToForward {
fn from((packet, delay_until): (MixPacket, Instant)) -> Self {
PacketToForward::new(packet, Some(delay_until))
}
}
impl PacketToForward {
pub fn new(packet: MixPacket, forward_delay_target: Option<Instant>) -> Self {
PacketToForward {
packet,
forward_delay_target,
} }
} }
pub fn new_no_delay(packet: MixPacket) -> Self {
Self::new(packet, None)
}
} }
@@ -1,7 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "client")]
pub mod client; pub mod client;
pub mod forwarder; pub mod forwarder;
#[cfg(feature = "client")]
pub use client::{Client, Config, SendWithoutResponse}; pub use client::{Client, Config, SendWithoutResponse};
@@ -32,7 +32,7 @@ impl Div<GasPrice> for Coin {
} }
} }
impl<'a> Div<GasPrice> for &'a Coin { impl Div<GasPrice> for &Coin {
type Output = Gas; type Output = Gas;
fn div(self, rhs: GasPrice) -> Self::Output { fn div(self, rhs: GasPrice) -> Self::Output {
@@ -22,7 +22,7 @@ pub struct GasPrice {
pub denom: String, pub denom: String,
} }
impl<'a> Mul<Gas> for &'a GasPrice { impl Mul<Gas> for &GasPrice {
type Output = Coin; type Output = Coin;
fn mul(self, gas_limit: Gas) -> Self::Output { fn mul(self, gas_limit: Gas) -> Self::Output {
@@ -32,7 +32,7 @@ pub(crate) mod string_rfc3339_offset_date_time {
struct Rfc3339OffsetDateTimeVisitor; struct Rfc3339OffsetDateTimeVisitor;
impl<'de> Visitor<'de> for Rfc3339OffsetDateTimeVisitor { impl Visitor<'_> for Rfc3339OffsetDateTimeVisitor {
type Value = OffsetDateTime; type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
@@ -7,7 +7,7 @@ use crate::ClientBandwidth;
use nym_credentials::ecash::utils::ecash_today; use nym_credentials::ecash::utils::ecash_today;
use nym_credentials_interface::Bandwidth; use nym_credentials_interface::Bandwidth;
use nym_gateway_requests::ServerResponse; use nym_gateway_requests::ServerResponse;
use nym_gateway_storage::Storage; use nym_gateway_storage::GatewayStorage;
use si_scale::helpers::bibytes2; use si_scale::helpers::bibytes2;
use time::OffsetDateTime; use time::OffsetDateTime;
use tracing::*; use tracing::*;
@@ -15,17 +15,17 @@ use tracing::*;
const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB
#[derive(Clone)] #[derive(Clone)]
pub struct BandwidthStorageManager<S> { pub struct BandwidthStorageManager {
pub(crate) storage: S, pub(crate) storage: GatewayStorage,
pub(crate) client_bandwidth: ClientBandwidth, pub(crate) client_bandwidth: ClientBandwidth,
pub(crate) client_id: i64, pub(crate) client_id: i64,
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig, pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
pub(crate) only_coconut_credentials: bool, pub(crate) only_coconut_credentials: bool,
} }
impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> { impl BandwidthStorageManager {
pub fn new( pub fn new(
storage: S, storage: GatewayStorage,
client_bandwidth: ClientBandwidth, client_bandwidth: ClientBandwidth,
client_id: i64, client_id: i64,
bandwidth_cfg: BandwidthFlushingBehaviourConfig, bandwidth_cfg: BandwidthFlushingBehaviourConfig,
@@ -111,7 +111,7 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
} }
#[instrument(level = "trace", skip_all)] #[instrument(level = "trace", skip_all)]
async fn sync_storage_bandwidth(&mut self) -> Result<()> { pub async fn sync_storage_bandwidth(&mut self) -> Result<()> {
trace!("syncing client bandwidth with the underlying storage"); trace!("syncing client bandwidth with the underlying storage");
let updated = self let updated = self
.storage .storage
@@ -8,8 +8,8 @@ use std::time::Duration;
use time::OffsetDateTime; use time::OffsetDateTime;
use tokio::sync::RwLock; use tokio::sync::RwLock;
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5); const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_secs(5 * 60); // 5 minutes
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 5 * 1024 * 1024; // 5MB
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct BandwidthFlushingBehaviourConfig { pub struct BandwidthFlushingBehaviourConfig {
@@ -13,7 +13,6 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody}; use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
use nym_credentials_interface::Bandwidth; use nym_credentials_interface::Bandwidth;
use nym_credentials_interface::{ClientTicket, TicketType}; use nym_credentials_interface::{ClientTicket, TicketType};
use nym_gateway_storage::Storage;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{ use nym_validator_client::nyxd::contract_traits::{
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient, EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
@@ -126,21 +125,18 @@ pub struct CredentialHandlerConfig {
pub maximum_time_between_redemption: Duration, pub maximum_time_between_redemption: Duration,
} }
pub(crate) struct CredentialHandler<St: Storage> { pub(crate) struct CredentialHandler {
config: CredentialHandlerConfig, config: CredentialHandlerConfig,
multisig_threshold: f32, multisig_threshold: f32,
ticket_receiver: UnboundedReceiver<ClientTicket>, ticket_receiver: UnboundedReceiver<ClientTicket>,
shared_state: SharedState<St>, shared_state: SharedState,
pending_tickets: Vec<PendingVerification>, pending_tickets: Vec<PendingVerification>,
pending_redemptions: Vec<PendingRedemptionVote>, pending_redemptions: Vec<PendingRedemptionVote>,
} }
impl<St> CredentialHandler<St> impl CredentialHandler {
where
St: Storage + Clone + 'static,
{
async fn rebuild_pending_tickets( async fn rebuild_pending_tickets(
shared_state: &SharedState<St>, shared_state: &SharedState,
) -> Result<Vec<PendingVerification>, EcashTicketError> { ) -> Result<Vec<PendingVerification>, EcashTicketError> {
// 1. get all tickets that were not fully verified // 1. get all tickets that were not fully verified
let unverified = shared_state.storage.get_all_unverified_tickets().await?; let unverified = shared_state.storage.get_all_unverified_tickets().await?;
@@ -188,7 +184,7 @@ where
} }
async fn rebuild_pending_votes( async fn rebuild_pending_votes(
shared_state: &SharedState<St>, shared_state: &SharedState,
) -> Result<Vec<PendingRedemptionVote>, EcashTicketError> { ) -> Result<Vec<PendingRedemptionVote>, EcashTicketError> {
// 1. get all tickets that were not fully verified // 1. get all tickets that were not fully verified
let unverified = shared_state.storage.get_all_unresolved_proposals().await?; let unverified = shared_state.storage.get_all_unresolved_proposals().await?;
@@ -259,7 +255,7 @@ where
pub(crate) async fn new( pub(crate) async fn new(
config: CredentialHandlerConfig, config: CredentialHandlerConfig,
ticket_receiver: UnboundedReceiver<ClientTicket>, ticket_receiver: UnboundedReceiver<ClientTicket>,
shared_state: SharedState<St>, shared_state: SharedState,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let multisig_threshold = shared_state let multisig_threshold = shared_state
.nyxd_client .nyxd_client
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_gateway_storage::error::StorageError; use nym_gateway_storage::error::GatewayStorageError;
use nym_validator_client::coconut::EcashApiError; use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::error::NyxdError; use nym_validator_client::nyxd::error::NyxdError;
@@ -37,7 +37,7 @@ pub enum EcashTicketError {
#[error("could not handle the ecash ticket due to internal storage failure: {source}")] #[error("could not handle the ecash ticket due to internal storage failure: {source}")]
InternalStorageFailure { InternalStorageFailure {
#[from] #[from]
source: StorageError, source: GatewayStorageError,
}, },
#[error("failed to create ticket redemption proposal: {source}")] #[error("failed to create ticket redemption proposal: {source}")]
@@ -8,7 +8,7 @@ use error::EcashTicketError;
use futures::channel::mpsc::{self, UnboundedSender}; use futures::channel::mpsc::{self, UnboundedSender};
use nym_credentials::CredentialSpendingData; use nym_credentials::CredentialSpendingData;
use nym_credentials_interface::{ClientTicket, CompactEcashError, NymPayInfo, VerificationKeyAuth}; use nym_credentials_interface::{ClientTicket, CompactEcashError, NymPayInfo, VerificationKeyAuth};
use nym_gateway_storage::Storage; use nym_gateway_storage::GatewayStorage;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::DirectSigningHttpRpcNyxdClient; use nym_validator_client::DirectSigningHttpRpcNyxdClient;
use state::SharedState; use state::SharedState;
@@ -23,24 +23,21 @@ mod state;
pub const TIME_RANGE_SEC: i64 = 30; pub const TIME_RANGE_SEC: i64 = 30;
pub struct EcashManager<S> { pub struct EcashManager {
shared_state: SharedState<S>, shared_state: SharedState,
pk_bytes: [u8; 32], // bytes representation of a pub key representing the verifier pk_bytes: [u8; 32], // bytes representation of a pub key representing the verifier
pay_infos: Mutex<Vec<NymPayInfo>>, pay_infos: Mutex<Vec<NymPayInfo>>,
cred_sender: UnboundedSender<ClientTicket>, cred_sender: UnboundedSender<ClientTicket>,
} }
impl<S> EcashManager<S> impl EcashManager {
where
S: Storage + Clone + 'static,
{
pub async fn new( pub async fn new(
credential_handler_cfg: CredentialHandlerConfig, credential_handler_cfg: CredentialHandlerConfig,
nyxd_client: DirectSigningHttpRpcNyxdClient, nyxd_client: DirectSigningHttpRpcNyxdClient,
pk_bytes: [u8; 32], pk_bytes: [u8; 32],
shutdown: nym_task::TaskClient, shutdown: nym_task::TaskClient,
storage: S, storage: GatewayStorage,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let shared_state = SharedState::new(nyxd_client, storage).await?; let shared_state = SharedState::new(nyxd_client, storage).await?;
@@ -66,7 +63,7 @@ where
self.shared_state.verification_key(epoch_id).await self.shared_state.verification_key(epoch_id).await
} }
pub fn storage(&self) -> &S { pub fn storage(&self) -> &GatewayStorage {
&self.shared_state.storage &self.shared_state.storage
} }
@@ -6,7 +6,7 @@ use crate::Error;
use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg}; use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg};
use nym_credentials_interface::VerificationKeyAuth; use nym_credentials_interface::VerificationKeyAuth;
use nym_ecash_contract_common::msg::ExecuteMsg; use nym_ecash_contract_common::msg::ExecuteMsg;
use nym_gateway_storage::Storage; use nym_gateway_storage::GatewayStorage;
use nym_validator_client::coconut::all_ecash_api_clients; use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{ use nym_validator_client::nyxd::contract_traits::{
@@ -23,20 +23,17 @@ use tracing::{error, trace, warn};
// state shared by different subtasks dealing with credentials // state shared by different subtasks dealing with credentials
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct SharedState<S> { pub(crate) struct SharedState {
pub(crate) nyxd_client: Arc<RwLock<DirectSigningHttpRpcNyxdClient>>, pub(crate) nyxd_client: Arc<RwLock<DirectSigningHttpRpcNyxdClient>>,
pub(crate) address: AccountId, pub(crate) address: AccountId,
pub(crate) epoch_data: Arc<RwLock<BTreeMap<EpochId, EpochState>>>, pub(crate) epoch_data: Arc<RwLock<BTreeMap<EpochId, EpochState>>>,
pub(crate) storage: S, pub(crate) storage: GatewayStorage,
} }
impl<S> SharedState<S> impl SharedState {
where
S: Storage + Clone,
{
pub(crate) async fn new( pub(crate) async fn new(
nyxd_client: DirectSigningHttpRpcNyxdClient, nyxd_client: DirectSigningHttpRpcNyxdClient,
storage: S, storage: GatewayStorage,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let address = nyxd_client.address(); let address = nyxd_client.address();
+1 -1
View File
@@ -39,7 +39,7 @@ pub enum Error {
OutOfBandwidth { required: i64, available: i64 }, OutOfBandwidth { required: i64, available: i64 },
#[error("Internal gateway storage error")] #[error("Internal gateway storage error")]
StorageError(#[from] nym_gateway_storage::error::StorageError), StorageError(#[from] nym_gateway_storage::error::GatewayStorageError),
#[error("{0}")] #[error("{0}")]
UnknownTicketType(#[from] nym_credentials_interface::UnknownTicketType), UnknownTicketType(#[from] nym_credentials_interface::UnknownTicketType),
+10 -12
View File
@@ -2,17 +2,15 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use bandwidth_storage_manager::BandwidthStorageManager; use bandwidth_storage_manager::BandwidthStorageManager;
use ecash::EcashManager;
use nym_credentials::ecash::utils::{cred_exp_date, ecash_today, EcashTime};
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
use nym_gateway_requests::models::CredentialSpendingRequest;
use std::sync::Arc; use std::sync::Arc;
use time::{Date, OffsetDateTime}; use time::{Date, OffsetDateTime};
use tracing::*; use tracing::*;
use nym_credentials::ecash::utils::{cred_exp_date, ecash_today, EcashTime};
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
use nym_gateway_requests::models::CredentialSpendingRequest;
use nym_gateway_storage::Storage;
pub use client_bandwidth::*; pub use client_bandwidth::*;
use ecash::EcashManager;
pub use error::*; pub use error::*;
pub mod bandwidth_storage_manager; pub mod bandwidth_storage_manager;
@@ -20,17 +18,17 @@ mod client_bandwidth;
pub mod ecash; pub mod ecash;
pub mod error; pub mod error;
pub struct CredentialVerifier<S> { pub struct CredentialVerifier {
credential: CredentialSpendingRequest, credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager<S>>, ecash_verifier: Arc<EcashManager>,
bandwidth_storage_manager: BandwidthStorageManager<S>, bandwidth_storage_manager: BandwidthStorageManager,
} }
impl<S: Storage + Clone + 'static> CredentialVerifier<S> { impl CredentialVerifier {
pub fn new( pub fn new(
credential: CredentialSpendingRequest, credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager<S>>, ecash_verifier: Arc<EcashManager>,
bandwidth_storage_manager: BandwidthStorageManager<S>, bandwidth_storage_manager: BandwidthStorageManager,
) -> Self { ) -> Self {
CredentialVerifier { CredentialVerifier {
credential, credential,
+2
View File
@@ -225,8 +225,10 @@ impl From<PayInfo> for NymPayInfo {
Clone, Clone,
Debug, Debug,
PartialEq, PartialEq,
Eq,
Serialize, Serialize,
Deserialize, Deserialize,
Hash,
strum::Display, strum::Display,
strum::EnumString, strum::EnumString,
strum::EnumIter, strum::EnumIter,
+1 -1
View File
@@ -41,6 +41,6 @@ aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"]
serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"] serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"]
asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"] asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"]
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"] hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"]
stream_cipher = ["aes", "ctr", "cipher", "generic-array"] stream_cipher = ["aes", "ctr", "cipher", "cipher/zeroize", "generic-array"]
sphinx = ["nym-sphinx-types/sphinx"] sphinx = ["nym-sphinx-types/sphinx"]
outfox = ["nym-sphinx-types/outfox"] outfox = ["nym-sphinx-types/outfox"]
+1 -2
View File
@@ -26,9 +26,8 @@ const PARALLEL_RUNS: usize = 32;
/// `lambda` ($\lambda$) in the DKG paper /// `lambda` ($\lambda$) in the DKG paper
const SECURITY_PARAMETER: usize = 256; const SECURITY_PARAMETER: usize = 256;
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
/// ceil(SECURITY_PARAMETER / PARALLEL_RUNS) in the paper /// ceil(SECURITY_PARAMETER / PARALLEL_RUNS) in the paper
const NUM_CHALLENGE_BITS: usize = (SECURITY_PARAMETER + PARALLEL_RUNS - 1) / PARALLEL_RUNS; const NUM_CHALLENGE_BITS: usize = SECURITY_PARAMETER.div_ceil(PARALLEL_RUNS);
// type alias for ease of use // type alias for ease of use
type FirstChallenge = Vec<Vec<Vec<u64>>>; type FirstChallenge = Vec<Vec<Vec<u64>>>;
+3 -3
View File
@@ -196,7 +196,7 @@ impl<'b> Add<&'b Polynomial> for Polynomial {
} }
} }
impl<'a> Add<Polynomial> for &'a Polynomial { impl Add<Polynomial> for &Polynomial {
type Output = Polynomial; type Output = Polynomial;
fn add(self, rhs: Polynomial) -> Polynomial { fn add(self, rhs: Polynomial) -> Polynomial {
@@ -212,10 +212,10 @@ impl Add<Polynomial> for Polynomial {
} }
} }
impl<'a, 'b> Add<&'b Polynomial> for &'a Polynomial { impl<'a> Add<&'a Polynomial> for &Polynomial {
type Output = Polynomial; type Output = Polynomial;
fn add(self, rhs: &'b Polynomial) -> Self::Output { fn add(self, rhs: &'a Polynomial) -> Self::Output {
let len = self.coefficients.len(); let len = self.coefficients.len();
let rhs_len = rhs.coefficients.len(); let rhs_len = rhs.coefficients.len();
@@ -37,7 +37,7 @@ pub struct GatewayHandshake<'a> {
handshake_future: BoxFuture<'a, Result<SharedGatewayKey, HandshakeError>>, handshake_future: BoxFuture<'a, Result<SharedGatewayKey, HandshakeError>>,
} }
impl<'a> Future for GatewayHandshake<'a> { impl Future for GatewayHandshake<'_> {
type Output = Result<SharedGatewayKey, HandshakeError>; type Output = Result<SharedGatewayKey, HandshakeError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+3
View File
@@ -16,12 +16,15 @@ sqlx = { workspace = true, features = [
"migrate", "migrate",
"time", "time",
] } ] }
strum = { workspace = true }
time = { workspace = true } time = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
nym-sphinx = { path = "../nymsphinx" } nym-sphinx = { path = "../nymsphinx" }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
nym-node-metrics = { path = "../../nym-node/nym-node-metrics" }
nym-statistics-common = { path = "../statistics" }
[build-dependencies] [build-dependencies]
+6 -8
View File
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use error::StatsStorageError; use error::StatsStorageError;
use models::{ActiveSession, FinishedSession, SessionType, StoredFinishedSession}; use models::StoredFinishedSession;
use nym_node_metrics::entry::{ActiveSession, FinishedSession, SessionType};
use nym_sphinx::DestinationAddressBytes; use nym_sphinx::DestinationAddressBytes;
use sessions::SessionManager; use sessions::SessionManager;
use sqlx::ConnectOptions; use sqlx::ConnectOptions;
@@ -70,8 +71,8 @@ impl PersistentStatsStorage {
.session_manager .session_manager
.insert_finished_session( .insert_finished_session(
date, date,
session.duration.whole_milliseconds() as i64, session.duration.as_millis() as i64,
session.typ.to_string().into(), session.typ.to_string(),
) )
.await?) .await?)
} }
@@ -125,7 +126,7 @@ impl PersistentStatsStorage {
.insert_active_session( .insert_active_session(
client_address.as_base58_string(), client_address.as_base58_string(),
session.start, session.start,
session.typ.to_string().into(), session.typ.to_string(),
) )
.await?) .await?)
} }
@@ -137,10 +138,7 @@ impl PersistentStatsStorage {
) -> Result<(), StatsStorageError> { ) -> Result<(), StatsStorageError> {
Ok(self Ok(self
.session_manager .session_manager
.update_active_session_type( .update_active_session_type(client_address.as_base58_string(), session_type.to_string())
client_address.as_base58_string(),
session_type.to_string().into(),
)
.await?) .await?)
} }
+18 -74
View File
@@ -1,9 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use nym_credentials_interface::TicketType; use nym_node_metrics::entry::{ActiveSession, FinishedSession, SessionType};
use sqlx::prelude::FromRow; use sqlx::prelude::FromRow;
use time::{Duration, OffsetDateTime}; use time::OffsetDateTime;
pub use nym_credentials_interface::TicketType;
#[derive(FromRow)] #[derive(FromRow)]
pub struct StoredFinishedSession { pub struct StoredFinishedSession {
@@ -11,52 +13,26 @@ pub struct StoredFinishedSession {
typ: String, typ: String,
} }
impl StoredFinishedSession { impl From<StoredFinishedSession> for FinishedSession {
pub fn serialize(&self) -> (u64, String) { fn from(value: StoredFinishedSession) -> Self {
( FinishedSession {
self.duration_ms as u64, //we are sure that it fits in a u64, see `fn end_at` duration: std::time::Duration::from_millis(value.duration_ms as u64),
self.typ.clone(), typ: SessionType::from_string(value.typ),
) }
} }
} }
pub struct FinishedSession { pub trait ToSessionType {
pub duration: Duration, fn to_session_type(&self) -> SessionType;
pub typ: SessionType,
} }
#[derive(PartialEq)] impl ToSessionType for TicketType {
pub enum SessionType { fn to_session_type(&self) -> SessionType {
Vpn,
Mixnet,
Unknown,
}
impl SessionType {
pub fn to_string(&self) -> &str {
match self { match self {
Self::Vpn => "vpn", TicketType::V1MixnetEntry => SessionType::Mixnet,
Self::Mixnet => "mixnet", TicketType::V1MixnetExit => SessionType::Mixnet,
Self::Unknown => "unknown", TicketType::V1WireguardEntry => SessionType::Vpn,
} TicketType::V1WireguardExit => SessionType::Vpn,
}
pub fn from_string(s: &str) -> Self {
match s {
"vpn" => Self::Vpn,
"mixnet" => Self::Mixnet,
_ => Self::Unknown,
}
}
}
impl From<TicketType> for SessionType {
fn from(value: TicketType) -> Self {
match value {
TicketType::V1MixnetEntry => Self::Mixnet,
TicketType::V1MixnetExit => Self::Mixnet,
TicketType::V1WireguardEntry => Self::Vpn,
TicketType::V1WireguardExit => Self::Vpn,
} }
} }
} }
@@ -67,38 +43,6 @@ pub(crate) struct StoredActiveSession {
typ: String, typ: String,
} }
pub struct ActiveSession {
pub start: OffsetDateTime,
pub typ: SessionType,
}
impl ActiveSession {
pub fn new(start_time: OffsetDateTime) -> Self {
ActiveSession {
start: start_time,
typ: SessionType::Unknown,
}
}
pub fn set_type(&mut self, ticket_type: TicketType) {
self.typ = ticket_type.into();
}
pub fn end_at(self, stop_time: OffsetDateTime) -> Option<FinishedSession> {
let session_duration = stop_time - self.start;
//ensure duration is positive to fit in a u64
//u64::max milliseconds is 500k millenia so no overflow issue
if session_duration > Duration::ZERO {
Some(FinishedSession {
duration: session_duration,
typ: self.typ,
})
} else {
None
}
}
}
impl From<StoredActiveSession> for ActiveSession { impl From<StoredActiveSession> for ActiveSession {
fn from(value: StoredActiveSession) -> Self { fn from(value: StoredActiveSession) -> Self {
ActiveSession { ActiveSession {
-1
View File
@@ -9,7 +9,6 @@ edition.workspace = true
license.workspace = true license.workspace = true
[dependencies] [dependencies]
async-trait = { workspace = true }
bincode = { workspace = true } bincode = { workspace = true }
defguard_wireguard_rs = { workspace = true } defguard_wireguard_rs = { workspace = true }
log = { workspace = true } log = { workspace = true }
+1 -1
View File
@@ -4,7 +4,7 @@
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum StorageError { pub enum GatewayStorageError {
#[error("Database experienced an internal error: {0}")] #[error("Database experienced an internal error: {0}")]
InternalDatabaseError(#[from] sqlx::Error), InternalDatabaseError(#[from] sqlx::Error),
+95 -287
View File
@@ -1,10 +1,8 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net> // Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use async_trait::async_trait;
use bandwidth::BandwidthManager; use bandwidth::BandwidthManager;
use clients::{ClientManager, ClientType}; use clients::{ClientManager, ClientType};
use error::StorageError;
use inboxes::InboxManager; use inboxes::InboxManager;
use models::{ use models::{
Client, PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage, Client, PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage,
@@ -29,237 +27,11 @@ mod shared_keys;
mod tickets; mod tickets;
mod wireguard_peers; mod wireguard_peers;
#[async_trait] pub use error::GatewayStorageError;
pub trait Storage: Send + Sync {
async fn get_mixnet_client_id(
&self,
client_address: DestinationAddressBytes,
) -> Result<i64, StorageError>;
/// Inserts provided derived shared keys into the database.
/// If keys previously existed for the provided client, they are overwritten with the new data.
///
/// # Arguments
///
/// * `client_address`: base58-encoded address of the client
/// * `shared_keys`:
/// - legacy: shared encryption (AES128CTR) and mac (hmac-blake3) derived shared keys to store.
/// - current: shared AES256-GCM-SIV keys
async fn insert_shared_keys(
&self,
client_address: DestinationAddressBytes,
shared_keys: &SharedGatewayKey,
) -> Result<i64, StorageError>;
/// Tries to retrieve shared keys stored for the particular client.
///
/// # Arguments
///
/// * `client_address`: address of the client
async fn get_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<Option<PersistedSharedKeys>, StorageError>;
/// Removes from the database shared keys derived with the particular client.
///
/// # Arguments
///
/// * `client_address`: address of the client
// currently there is no code flow that causes removal (not overwriting)
// of the stored keys. However, retain the function for consistency and completion sake
#[allow(dead_code)]
async fn remove_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), StorageError>;
/// Tries to retrieve a particular client.
///
/// # Arguments
///
/// * `client_id`: id of the client
#[allow(dead_code)]
async fn get_client(&self, client_id: i64) -> Result<Option<Client>, StorageError>;
/// Inserts new message to the storage for an offline client for future retrieval.
///
/// # Arguments
///
/// * `client_address`: address of the client
/// * `message`: raw message to store.
async fn store_message(
&self,
client_address: DestinationAddressBytes,
message: Vec<u8>,
) -> Result<(), StorageError>;
/// Retrieves messages stored for the particular client specified by the provided address.
///
/// # Arguments
///
/// * `client_address`: address of the client
/// * `start_after`: optional starting id of the messages to grab
///
/// returns the retrieved messages alongside optional id of the last message retrieved if
/// there are more messages to retrieve.
async fn retrieve_messages(
&self,
client_address: DestinationAddressBytes,
start_after: Option<i64>,
) -> Result<(Vec<StoredMessage>, Option<i64>), StorageError>;
/// Removes messages with the specified ids
///
/// # Arguments
///
/// * `ids`: ids of the messages to remove
async fn remove_messages(&self, ids: Vec<i64>) -> Result<(), StorageError>;
/// Creates a new bandwidth entry for the particular client.
async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), StorageError>;
/// Set the freepass expiration date of the particular client to the provided date.
///
/// # Arguments
///
/// * `client_address`: address of the client
/// * `expiration`: the expiration date of the associated free pass.
async fn set_expiration(
&self,
client_id: i64,
expiration: OffsetDateTime,
) -> Result<(), StorageError>;
/// Reset all the bandwidth
///
/// # Arguments
///
/// * `client_address`: address of the client
async fn reset_bandwidth(&self, client_id: i64) -> Result<(), StorageError>;
/// Tries to retrieve available bandwidth for the particular client.
async fn get_available_bandwidth(
&self,
client_id: i64,
) -> Result<Option<PersistedBandwidth>, StorageError>;
/// Increases specified client's bandwidth by the provided amount and returns the current value.
async fn increase_bandwidth(&self, client_id: i64, amount: i64) -> Result<i64, StorageError>;
async fn revoke_ticket_bandwidth(
&self,
ticket_id: i64,
amount: i64,
) -> Result<(), StorageError>;
#[allow(dead_code)]
/// Decreases specified client's bandwidth by the provided amount and returns the current value.
async fn decrease_bandwidth(&self, client_id: i64, amount: i64) -> Result<i64, StorageError>;
async fn insert_epoch_signers(
&self,
epoch_id: i64,
signer_ids: Vec<i64>,
) -> Result<(), StorageError>;
async fn insert_received_ticket(
&self,
client_id: i64,
received_at: OffsetDateTime,
serial_number: Vec<u8>,
data: Vec<u8>,
) -> Result<i64, StorageError>;
// note: this only checks very recent tickets that haven't yet been redeemed
// (but it's better than nothing)
/// Check if the ticket with the provided serial number if already present in the storage.
///
/// # Arguments
///
/// * `serial_number`: the unique serial number embedded in the ticket
async fn contains_ticket(&self, serial_number: &[u8]) -> Result<bool, StorageError>;
async fn insert_ticket_verification(
&self,
ticket_id: i64,
signer_id: i64,
verified_at: OffsetDateTime,
accepted: bool,
) -> Result<(), StorageError>;
async fn update_rejected_ticket(&self, ticket_id: i64) -> Result<(), StorageError>;
async fn update_verified_ticket(&self, ticket_id: i64) -> Result<(), StorageError>;
async fn remove_verified_ticket_binary_data(&self, ticket_id: i64) -> Result<(), StorageError>;
async fn get_all_verified_tickets_with_sn(&self) -> Result<Vec<VerifiedTicket>, StorageError>;
async fn get_all_proposed_tickets_with_sn(
&self,
proposal_id: u32,
) -> Result<Vec<VerifiedTicket>, StorageError>;
async fn insert_redemption_proposal(
&self,
tickets: &[VerifiedTicket],
proposal_id: u32,
created_at: OffsetDateTime,
) -> Result<(), StorageError>;
async fn clear_post_proposal_data(
&self,
proposal_id: u32,
resolved_at: OffsetDateTime,
rejected: bool,
) -> Result<(), StorageError>;
async fn latest_proposal(&self) -> Result<Option<RedemptionProposal>, StorageError>;
async fn get_all_unverified_tickets(&self) -> Result<Vec<ClientTicket>, StorageError>;
async fn get_all_unresolved_proposals(&self) -> Result<Vec<i64>, StorageError>;
async fn get_votes(&self, ticket_id: i64) -> Result<Vec<i64>, StorageError>;
async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, StorageError>;
/// Insert a wireguard peer in the storage.
///
/// # Arguments
///
/// * `peer`: wireguard peer data to be stored
/// * `with_client_id`: if the peer should have a corresponding client_id
/// (created with entry wireguard ticket) or live without one (or with an
/// exiting one), for temporary backwards compatibility.
async fn insert_wireguard_peer(
&self,
peer: &defguard_wireguard_rs::host::Peer,
with_client_id: bool,
) -> Result<Option<i64>, StorageError>;
/// Tries to retrieve available bandwidth for the particular peer.
///
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be retrieved.
async fn get_wireguard_peer(
&self,
peer_public_key: &str,
) -> Result<Option<WireguardPeer>, StorageError>;
/// Retrieves all wireguard peers.
async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, StorageError>;
/// Remove a wireguard peer from the storage.
///
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be removed.
async fn remove_wireguard_peer(&self, peer_public_key: &str) -> Result<(), StorageError>;
}
// note that clone here is fine as upon cloning the same underlying pool will be used // note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)] #[derive(Clone)]
pub struct PersistentStorage { pub struct GatewayStorage {
client_manager: ClientManager, client_manager: ClientManager,
shared_key_manager: SharedKeysManager, shared_key_manager: SharedKeysManager,
inbox_manager: InboxManager, inbox_manager: InboxManager,
@@ -268,7 +40,7 @@ pub struct PersistentStorage {
wireguard_peer_manager: wireguard_peers::WgPeerManager, wireguard_peer_manager: wireguard_peers::WgPeerManager,
} }
impl PersistentStorage { impl GatewayStorage {
/// Initialises `PersistentStorage` using the provided path. /// Initialises `PersistentStorage` using the provided path.
/// ///
/// # Arguments /// # Arguments
@@ -278,7 +50,7 @@ impl PersistentStorage {
pub async fn init<P: AsRef<Path> + Send>( pub async fn init<P: AsRef<Path> + Send>(
database_path: P, database_path: P,
message_retrieval_limit: i64, message_retrieval_limit: i64,
) -> Result<Self, StorageError> { ) -> Result<Self, GatewayStorageError> {
debug!( debug!(
"Attempting to connect to database {:?}", "Attempting to connect to database {:?}",
database_path.as_ref().as_os_str() database_path.as_ref().as_os_str()
@@ -307,7 +79,7 @@ impl PersistentStorage {
} }
// the cloning here are cheap as connection pool is stored behind an Arc // the cloning here are cheap as connection pool is stored behind an Arc
Ok(PersistentStorage { Ok(GatewayStorage {
client_manager: clients::ClientManager::new(connection_pool.clone()), client_manager: clients::ClientManager::new(connection_pool.clone()),
wireguard_peer_manager: wireguard_peers::WgPeerManager::new(connection_pool.clone()), wireguard_peer_manager: wireguard_peers::WgPeerManager::new(connection_pool.clone()),
shared_key_manager: SharedKeysManager::new(connection_pool.clone()), shared_key_manager: SharedKeysManager::new(connection_pool.clone()),
@@ -318,23 +90,22 @@ impl PersistentStorage {
} }
} }
#[async_trait] impl GatewayStorage {
impl Storage for PersistentStorage { pub async fn get_mixnet_client_id(
async fn get_mixnet_client_id(
&self, &self,
client_address: DestinationAddressBytes, client_address: DestinationAddressBytes,
) -> Result<i64, StorageError> { ) -> Result<i64, GatewayStorageError> {
Ok(self Ok(self
.shared_key_manager .shared_key_manager
.client_id(&client_address.as_base58_string()) .client_id(&client_address.as_base58_string())
.await?) .await?)
} }
async fn insert_shared_keys( pub async fn insert_shared_keys(
&self, &self,
client_address: DestinationAddressBytes, client_address: DestinationAddressBytes,
shared_keys: &SharedGatewayKey, shared_keys: &SharedGatewayKey,
) -> Result<i64, StorageError> { ) -> Result<i64, GatewayStorageError> {
let client_address_bs58 = client_address.as_base58_string(); let client_address_bs58 = client_address.as_base58_string();
let client_id = match self let client_id = match self
.shared_key_manager .shared_key_manager
@@ -359,10 +130,10 @@ impl Storage for PersistentStorage {
Ok(client_id) Ok(client_id)
} }
async fn get_shared_keys( pub async fn get_shared_keys(
&self, &self,
client_address: DestinationAddressBytes, client_address: DestinationAddressBytes,
) -> Result<Option<PersistedSharedKeys>, StorageError> { ) -> Result<Option<PersistedSharedKeys>, GatewayStorageError> {
let keys = self let keys = self
.shared_key_manager .shared_key_manager
.get_shared_keys(&client_address.as_base58_string()) .get_shared_keys(&client_address.as_base58_string())
@@ -371,37 +142,37 @@ impl Storage for PersistentStorage {
} }
#[allow(dead_code)] #[allow(dead_code)]
async fn remove_shared_keys( pub async fn remove_shared_keys(
&self, &self,
client_address: DestinationAddressBytes, client_address: DestinationAddressBytes,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
self.shared_key_manager self.shared_key_manager
.remove_shared_keys(&client_address.as_base58_string()) .remove_shared_keys(&client_address.as_base58_string())
.await?; .await?;
Ok(()) Ok(())
} }
async fn get_client(&self, client_id: i64) -> Result<Option<Client>, StorageError> { pub async fn get_client(&self, client_id: i64) -> Result<Option<Client>, GatewayStorageError> {
let client = self.client_manager.get_client(client_id).await?; let client = self.client_manager.get_client(client_id).await?;
Ok(client) Ok(client)
} }
async fn store_message( pub async fn store_message(
&self, &self,
client_address: DestinationAddressBytes, client_address: DestinationAddressBytes,
message: Vec<u8>, message: Vec<u8>,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
self.inbox_manager self.inbox_manager
.insert_message(&client_address.as_base58_string(), message) .insert_message(&client_address.as_base58_string(), message)
.await?; .await?;
Ok(()) Ok(())
} }
async fn retrieve_messages( pub async fn retrieve_messages(
&self, &self,
client_address: DestinationAddressBytes, client_address: DestinationAddressBytes,
start_after: Option<i64>, start_after: Option<i64>,
) -> Result<(Vec<StoredMessage>, Option<i64>), StorageError> { ) -> Result<(Vec<StoredMessage>, Option<i64>), GatewayStorageError> {
let messages = self let messages = self
.inbox_manager .inbox_manager
.get_messages(&client_address.as_base58_string(), start_after) .get_messages(&client_address.as_base58_string(), start_after)
@@ -409,87 +180,95 @@ impl Storage for PersistentStorage {
Ok(messages) Ok(messages)
} }
async fn remove_messages(&self, ids: Vec<i64>) -> Result<(), StorageError> { pub async fn remove_messages(&self, ids: Vec<i64>) -> Result<(), GatewayStorageError> {
for id in ids { for id in ids {
self.inbox_manager.remove_message(id).await?; self.inbox_manager.remove_message(id).await?;
} }
Ok(()) Ok(())
} }
async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), StorageError> { pub async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), GatewayStorageError> {
self.bandwidth_manager.insert_new_client(client_id).await?; self.bandwidth_manager.insert_new_client(client_id).await?;
Ok(()) Ok(())
} }
async fn set_expiration( pub async fn set_expiration(
&self, &self,
client_id: i64, client_id: i64,
expiration: OffsetDateTime, expiration: OffsetDateTime,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
self.bandwidth_manager self.bandwidth_manager
.set_expiration(client_id, expiration) .set_expiration(client_id, expiration)
.await?; .await?;
Ok(()) Ok(())
} }
async fn reset_bandwidth(&self, client_id: i64) -> Result<(), StorageError> { pub async fn reset_bandwidth(&self, client_id: i64) -> Result<(), GatewayStorageError> {
self.bandwidth_manager.reset_bandwidth(client_id).await?; self.bandwidth_manager.reset_bandwidth(client_id).await?;
Ok(()) Ok(())
} }
async fn get_available_bandwidth( pub async fn get_available_bandwidth(
&self, &self,
client_id: i64, client_id: i64,
) -> Result<Option<PersistedBandwidth>, StorageError> { ) -> Result<Option<PersistedBandwidth>, GatewayStorageError> {
Ok(self Ok(self
.bandwidth_manager .bandwidth_manager
.get_available_bandwidth(client_id) .get_available_bandwidth(client_id)
.await?) .await?)
} }
async fn increase_bandwidth(&self, client_id: i64, amount: i64) -> Result<i64, StorageError> { pub async fn increase_bandwidth(
&self,
client_id: i64,
amount: i64,
) -> Result<i64, GatewayStorageError> {
Ok(self Ok(self
.bandwidth_manager .bandwidth_manager
.increase_bandwidth(client_id, amount) .increase_bandwidth(client_id, amount)
.await?) .await?)
} }
async fn revoke_ticket_bandwidth( pub async fn revoke_ticket_bandwidth(
&self, &self,
ticket_id: i64, ticket_id: i64,
amount: i64, amount: i64,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
Ok(self Ok(self
.bandwidth_manager .bandwidth_manager
.revoke_ticket_bandwidth(ticket_id, amount) .revoke_ticket_bandwidth(ticket_id, amount)
.await?) .await?)
} }
async fn decrease_bandwidth(&self, client_id: i64, amount: i64) -> Result<i64, StorageError> { pub async fn decrease_bandwidth(
&self,
client_id: i64,
amount: i64,
) -> Result<i64, GatewayStorageError> {
Ok(self Ok(self
.bandwidth_manager .bandwidth_manager
.decrease_bandwidth(client_id, amount) .decrease_bandwidth(client_id, amount)
.await?) .await?)
} }
async fn insert_epoch_signers( pub async fn insert_epoch_signers(
&self, &self,
epoch_id: i64, epoch_id: i64,
signer_ids: Vec<i64>, signer_ids: Vec<i64>,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
self.ticket_manager self.ticket_manager
.insert_ecash_signers(epoch_id, signer_ids) .insert_ecash_signers(epoch_id, signer_ids)
.await?; .await?;
Ok(()) Ok(())
} }
async fn insert_received_ticket( pub async fn insert_received_ticket(
&self, &self,
client_id: i64, client_id: i64,
received_at: OffsetDateTime, received_at: OffsetDateTime,
serial_number: Vec<u8>, serial_number: Vec<u8>,
data: Vec<u8>, data: Vec<u8>,
) -> Result<i64, StorageError> { ) -> Result<i64, GatewayStorageError> {
// technically if we crash between those 2 calls we'll have a bit of data inconsistency, // technically if we crash between those 2 calls we'll have a bit of data inconsistency,
// but nothing too tragic. we just won't get paid for a single ticket // but nothing too tragic. we just won't get paid for a single ticket
let ticket_id = self let ticket_id = self
@@ -503,24 +282,24 @@ impl Storage for PersistentStorage {
Ok(ticket_id) Ok(ticket_id)
} }
async fn contains_ticket(&self, serial_number: &[u8]) -> Result<bool, StorageError> { pub async fn contains_ticket(&self, serial_number: &[u8]) -> Result<bool, GatewayStorageError> {
Ok(self.ticket_manager.has_ticket_data(serial_number).await?) Ok(self.ticket_manager.has_ticket_data(serial_number).await?)
} }
async fn insert_ticket_verification( pub async fn insert_ticket_verification(
&self, &self,
ticket_id: i64, ticket_id: i64,
signer_id: i64, signer_id: i64,
verified_at: OffsetDateTime, verified_at: OffsetDateTime,
accepted: bool, accepted: bool,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
self.ticket_manager self.ticket_manager
.insert_ticket_verification(ticket_id, signer_id, verified_at, accepted) .insert_ticket_verification(ticket_id, signer_id, verified_at, accepted)
.await?; .await?;
Ok(()) Ok(())
} }
async fn update_rejected_ticket(&self, ticket_id: i64) -> Result<(), StorageError> { pub async fn update_rejected_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
// set the ticket as rejected // set the ticket as rejected
self.ticket_manager.set_rejected_ticket(ticket_id).await?; self.ticket_manager.set_rejected_ticket(ticket_id).await?;
@@ -531,7 +310,7 @@ impl Storage for PersistentStorage {
Ok(()) Ok(())
} }
async fn update_verified_ticket(&self, ticket_id: i64) -> Result<(), StorageError> { pub async fn update_verified_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
// 1. insert into verified table // 1. insert into verified table
self.ticket_manager self.ticket_manager
.insert_verified_ticket(ticket_id) .insert_verified_ticket(ticket_id)
@@ -545,36 +324,41 @@ impl Storage for PersistentStorage {
Ok(()) Ok(())
} }
async fn remove_verified_ticket_binary_data(&self, ticket_id: i64) -> Result<(), StorageError> { pub async fn remove_verified_ticket_binary_data(
&self,
ticket_id: i64,
) -> Result<(), GatewayStorageError> {
self.ticket_manager self.ticket_manager
.remove_binary_ticket_data(ticket_id) .remove_binary_ticket_data(ticket_id)
.await?; .await?;
Ok(()) Ok(())
} }
async fn get_all_verified_tickets_with_sn(&self) -> Result<Vec<VerifiedTicket>, StorageError> { pub async fn get_all_verified_tickets_with_sn(
&self,
) -> Result<Vec<VerifiedTicket>, GatewayStorageError> {
Ok(self Ok(self
.ticket_manager .ticket_manager
.get_all_verified_tickets_with_sn() .get_all_verified_tickets_with_sn()
.await?) .await?)
} }
async fn get_all_proposed_tickets_with_sn( pub async fn get_all_proposed_tickets_with_sn(
&self, &self,
proposal_id: u32, proposal_id: u32,
) -> Result<Vec<VerifiedTicket>, StorageError> { ) -> Result<Vec<VerifiedTicket>, GatewayStorageError> {
Ok(self Ok(self
.ticket_manager .ticket_manager
.get_all_proposed_tickets_with_sn(proposal_id as i64) .get_all_proposed_tickets_with_sn(proposal_id as i64)
.await?) .await?)
} }
async fn insert_redemption_proposal( pub async fn insert_redemption_proposal(
&self, &self,
tickets: &[VerifiedTicket], tickets: &[VerifiedTicket],
proposal_id: u32, proposal_id: u32,
created_at: OffsetDateTime, created_at: OffsetDateTime,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
// if we crash between those, there might a bit of an issue. we should revisit it later // if we crash between those, there might a bit of an issue. we should revisit it later
// 1. insert the actual proposal // 1. insert the actual proposal
@@ -592,12 +376,12 @@ impl Storage for PersistentStorage {
Ok(()) Ok(())
} }
async fn clear_post_proposal_data( pub async fn clear_post_proposal_data(
&self, &self,
proposal_id: u32, proposal_id: u32,
resolved_at: OffsetDateTime, resolved_at: OffsetDateTime,
rejected: bool, rejected: bool,
) -> Result<(), StorageError> { ) -> Result<(), GatewayStorageError> {
// 1. update proposal metadata // 1. update proposal metadata
self.ticket_manager self.ticket_manager
.update_redemption_proposal(proposal_id as i64, resolved_at, rejected) .update_redemption_proposal(proposal_id as i64, resolved_at, rejected)
@@ -616,11 +400,13 @@ impl Storage for PersistentStorage {
Ok(()) Ok(())
} }
async fn latest_proposal(&self) -> Result<Option<RedemptionProposal>, StorageError> { pub async fn latest_proposal(&self) -> Result<Option<RedemptionProposal>, GatewayStorageError> {
Ok(self.ticket_manager.get_latest_redemption_proposal().await?) Ok(self.ticket_manager.get_latest_redemption_proposal().await?)
} }
async fn get_all_unverified_tickets(&self) -> Result<Vec<ClientTicket>, StorageError> { pub async fn get_all_unverified_tickets(
&self,
) -> Result<Vec<ClientTicket>, GatewayStorageError> {
self.ticket_manager self.ticket_manager
.get_unverified_tickets() .get_unverified_tickets()
.await? .await?
@@ -629,29 +415,37 @@ impl Storage for PersistentStorage {
.collect() .collect()
} }
async fn get_all_unresolved_proposals(&self) -> Result<Vec<i64>, StorageError> { pub async fn get_all_unresolved_proposals(&self) -> Result<Vec<i64>, GatewayStorageError> {
Ok(self Ok(self
.ticket_manager .ticket_manager
.get_all_unresolved_redemption_proposal_ids() .get_all_unresolved_redemption_proposal_ids()
.await?) .await?)
} }
async fn get_votes(&self, ticket_id: i64) -> Result<Vec<i64>, StorageError> { pub async fn get_votes(&self, ticket_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
Ok(self Ok(self
.ticket_manager .ticket_manager
.get_verification_votes(ticket_id) .get_verification_votes(ticket_id)
.await?) .await?)
} }
async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, StorageError> { pub async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
Ok(self.ticket_manager.get_epoch_signers(epoch_id).await?) Ok(self.ticket_manager.get_epoch_signers(epoch_id).await?)
} }
async fn insert_wireguard_peer( /// Insert a wireguard peer in the storage.
///
/// # Arguments
///
/// * `peer`: wireguard peer data to be stored
/// * `with_client_id`: if the peer should have a corresponding client_id
/// (created with entry wireguard ticket) or live without one (or with an
/// exiting one), for temporary backwards compatibility.
pub async fn insert_wireguard_peer(
&self, &self,
peer: &defguard_wireguard_rs::host::Peer, peer: &defguard_wireguard_rs::host::Peer,
with_client_id: bool, with_client_id: bool,
) -> Result<Option<i64>, StorageError> { ) -> Result<Option<i64>, GatewayStorageError> {
let client_id = match self let client_id = match self
.wireguard_peer_manager .wireguard_peer_manager
.retrieve_peer(&peer.public_key.to_string()) .retrieve_peer(&peer.public_key.to_string())
@@ -676,10 +470,15 @@ impl Storage for PersistentStorage {
Ok(client_id) Ok(client_id)
} }
async fn get_wireguard_peer( /// Tries to retrieve available bandwidth for the particular peer.
///
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be retrieved.
pub async fn get_wireguard_peer(
&self, &self,
peer_public_key: &str, peer_public_key: &str,
) -> Result<Option<WireguardPeer>, StorageError> { ) -> Result<Option<WireguardPeer>, GatewayStorageError> {
let peer = self let peer = self
.wireguard_peer_manager .wireguard_peer_manager
.retrieve_peer(peer_public_key) .retrieve_peer(peer_public_key)
@@ -687,12 +486,21 @@ impl Storage for PersistentStorage {
Ok(peer) Ok(peer)
} }
async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, StorageError> { /// Retrieves all wireguard peers.
pub async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, GatewayStorageError> {
let ret = self.wireguard_peer_manager.retrieve_all_peers().await?; let ret = self.wireguard_peer_manager.retrieve_all_peers().await?;
Ok(ret) Ok(ret)
} }
async fn remove_wireguard_peer(&self, peer_public_key: &str) -> Result<(), StorageError> { /// Remove a wireguard peer from the storage.
///
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be removed.
pub async fn remove_wireguard_peer(
&self,
peer_public_key: &str,
) -> Result<(), GatewayStorageError> {
self.wireguard_peer_manager self.wireguard_peer_manager
.remove_peer(peer_public_key) .remove_peer(peer_public_key)
.await?; .await?;
+8 -8
View File
@@ -1,7 +1,7 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::error::StorageError; use crate::error::GatewayStorageError;
use nym_credentials_interface::{AvailableBandwidth, ClientTicket, CredentialSpendingData}; use nym_credentials_interface::{AvailableBandwidth, ClientTicket, CredentialSpendingData};
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey}; use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
use sqlx::FromRow; use sqlx::FromRow;
@@ -24,24 +24,24 @@ pub struct PersistedSharedKeys {
} }
impl TryFrom<PersistedSharedKeys> for SharedGatewayKey { impl TryFrom<PersistedSharedKeys> for SharedGatewayKey {
type Error = StorageError; type Error = GatewayStorageError;
fn try_from(value: PersistedSharedKeys) -> Result<Self, Self::Error> { fn try_from(value: PersistedSharedKeys) -> Result<Self, Self::Error> {
match ( match (
&value.derived_aes256_gcm_siv_key, &value.derived_aes256_gcm_siv_key,
&value.derived_aes128_ctr_blake3_hmac_keys_bs58, &value.derived_aes128_ctr_blake3_hmac_keys_bs58,
) { ) {
(None, None) => Err(StorageError::MissingSharedKey { (None, None) => Err(GatewayStorageError::MissingSharedKey {
id: value.client_id, id: value.client_id,
}), }),
(Some(aes256gcm_siv), _) => { (Some(aes256gcm_siv), _) => {
let current_key = SharedSymmetricKey::try_from_bytes(aes256gcm_siv) let current_key = SharedSymmetricKey::try_from_bytes(aes256gcm_siv)
.map_err(|source| StorageError::DataCorruption(source.to_string()))?; .map_err(|source| GatewayStorageError::DataCorruption(source.to_string()))?;
Ok(SharedGatewayKey::Current(current_key)) Ok(SharedGatewayKey::Current(current_key))
} }
(None, Some(aes128ctr_hmac)) => { (None, Some(aes128ctr_hmac)) => {
let legacy_key = LegacySharedKeys::try_from_base58_string(aes128ctr_hmac) let legacy_key = LegacySharedKeys::try_from_base58_string(aes128ctr_hmac)
.map_err(|source| StorageError::DataCorruption(source.to_string()))?; .map_err(|source| GatewayStorageError::DataCorruption(source.to_string()))?;
Ok(SharedGatewayKey::Legacy(legacy_key)) Ok(SharedGatewayKey::Legacy(legacy_key))
} }
} }
@@ -91,12 +91,12 @@ pub struct UnverifiedTicketData {
} }
impl TryFrom<UnverifiedTicketData> for ClientTicket { impl TryFrom<UnverifiedTicketData> for ClientTicket {
type Error = StorageError; type Error = GatewayStorageError;
fn try_from(value: UnverifiedTicketData) -> Result<Self, Self::Error> { fn try_from(value: UnverifiedTicketData) -> Result<Self, Self::Error> {
Ok(ClientTicket { Ok(ClientTicket {
spending_data: CredentialSpendingData::try_from_bytes(&value.data).map_err(|_| { spending_data: CredentialSpendingData::try_from_bytes(&value.data).map_err(|_| {
StorageError::MalformedStoredTicketData { GatewayStorageError::MalformedStoredTicketData {
ticket_id: value.ticket_id, ticket_id: value.ticket_id,
} }
})?, })?,
@@ -152,7 +152,7 @@ impl From<defguard_wireguard_rs::host::Peer> for WireguardPeer {
} }
impl TryFrom<WireguardPeer> for defguard_wireguard_rs::host::Peer { impl TryFrom<WireguardPeer> for defguard_wireguard_rs::host::Peer {
type Error = crate::error::StorageError; type Error = crate::error::GatewayStorageError;
fn try_from(value: WireguardPeer) -> Result<Self, Self::Error> { fn try_from(value: WireguardPeer) -> Result<Self, Self::Error> {
Ok(Self { Ok(Self {
+115 -2
View File
@@ -1,12 +1,13 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::fmt; use std::{fmt, str::FromStr};
use http::HeaderValue; use http::HeaderValue;
use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned}; use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct UserAgent { pub struct UserAgent {
pub application: String, pub application: String,
pub version: String, pub version: String,
@@ -14,6 +15,36 @@ pub struct UserAgent {
pub git_commit: String, pub git_commit: String,
} }
#[derive(Clone, Debug, thiserror::Error)]
#[error("invalid user agent string: {0}")]
pub struct UserAgentError(String);
impl FromStr for UserAgent {
type Err = UserAgentError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split('/').collect();
if parts.len() != 4 {
return Err(UserAgentError(s.to_string()));
}
Ok(UserAgent {
application: parts[0].to_string(),
version: parts[1].to_string(),
platform: parts[2].to_string(),
git_commit: parts[3].to_string(),
})
}
}
impl TryFrom<&str> for UserAgent {
type Error = UserAgentError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
UserAgent::from_str(s)
}
}
impl fmt::Display for UserAgent { impl fmt::Display for UserAgent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abbreviated_commit = self.git_commit.chars().take(7).collect::<String>(); let abbreviated_commit = self.git_commit.chars().take(7).collect::<String>();
@@ -54,3 +85,85 @@ impl From<BinaryBuildInformationOwned> for UserAgent {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parsing_valid_user_agent() {
let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg";
let parsed = UserAgent::from_str(user_agent).unwrap();
assert_eq!(
parsed,
UserAgent {
application: "nym-mixnode".to_string(),
version: "0.11.0".to_string(),
platform: "x86_64-unknown-linux-gnu".to_string(),
git_commit: "abcdefg".to_string()
}
);
}
#[test]
fn parsing_invalid_user_agent() {
let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu";
assert!(UserAgent::from_str(user_agent).is_err());
}
#[test]
fn converting_user_agent_to_string() {
let user_agent = UserAgent {
application: "nym-mixnode".to_string(),
version: "0.11.0".to_string(),
platform: "x86_64-unknown-linux-gnu".to_string(),
git_commit: "abcdefg".to_string(),
};
assert_eq!(
user_agent.to_string(),
"nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg"
);
}
#[test]
fn converting_user_agent_to_display() {
let user_agent = UserAgent {
application: "nym-mixnode".to_string(),
version: "0.11.0".to_string(),
platform: "x86_64-unknown-linux-gnu".to_string(),
git_commit: "abcdefg".to_string(),
};
assert_eq!(
format!("{}", user_agent),
"nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg"
);
}
#[test]
fn converting_user_agent_to_header_value_fails() {
let user_agent = UserAgent {
application: "nym-mixnode".to_string(),
version: "0.11.0".to_string(),
platform: "x86_64-unknown-linux-gnu".to_string(),
git_commit: "abcdefg".to_string(),
};
let header_value: Result<HeaderValue, _> = user_agent.clone().try_into();
assert!(header_value.is_ok());
}
#[test]
fn converting_user_agent_to_header_value_has_same_string_representation() {
let user_agent = UserAgent {
application: "nym-mixnode".to_string(),
version: "0.11.0".to_string(),
platform: "x86_64-unknown-linux-gnu".to_string(),
git_commit: "abcdefg".to_string(),
};
let header_value: HeaderValue = user_agent.clone().try_into().unwrap();
assert_eq!(header_value.to_str().unwrap(), user_agent.to_string());
}
}
+2 -4
View File
@@ -26,7 +26,7 @@ url = { workspace = true }
time.workspace = true time.workspace = true
thiserror = { workspace = true } thiserror = { workspace = true }
nym-crypto = { path = "../crypto" } nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" } nym-network-defaults = { path = "../network-defaults" }
nym-sphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" } nym-sphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" }
nym-sphinx-addressing = { path = "../nymsphinx/addressing" } nym-sphinx-addressing = { path = "../nymsphinx/addressing" }
@@ -35,7 +35,5 @@ nym-sphinx-framing = { path = "../nymsphinx/framing" }
nym-sphinx-params = { path = "../nymsphinx/params" } nym-sphinx-params = { path = "../nymsphinx/params" }
nym-sphinx-types = { path = "../nymsphinx/types" } nym-sphinx-types = { path = "../nymsphinx/types" }
nym-task = { path = "../task" } nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-bin-common = { path = "../bin-common" }
nym-metrics = { path = "../nym-metrics" } nym-metrics = { path = "../nym-metrics" }
nym-node-http-api = { path = "../../nym-node/nym-node-http-api" }
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod packet_processor; pub mod packet_processor;
pub mod verloc;
-81
View File
@@ -1,81 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::{self, Display, Formatter};
use std::io;
#[derive(Debug)]
pub enum RttError {
UnexpectedEchoPacketSize,
UnexpectedReplyPacketSize,
MalformedSenderIdentity,
MalformedEchoSignature,
MalformedReplySignature,
InvalidEchoSignature,
InvalidReplySignature,
UnreachableNode(String, io::Error),
UnexpectedConnectionFailureWrite(String, io::Error),
UnexpectedConnectionFailureRead(String, io::Error),
ConnectionReadTimeout(String),
ConnectionWriteTimeout(String),
UnexpectedReplySequence,
ShutdownReceived,
}
impl Display for RttError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
RttError::UnexpectedEchoPacketSize => {
write!(f, "The received echo packet had unexpected size")
}
RttError::UnexpectedReplyPacketSize => {
write!(f, "The received reply packet had unexpected size")
}
RttError::MalformedSenderIdentity => {
write!(f, "The received echo packet had malformed sender")
}
RttError::MalformedEchoSignature => {
write!(f, "The received echo packet had malformed signature")
}
RttError::MalformedReplySignature => {
write!(f, "The received reply packet had malformed signature")
}
RttError::InvalidEchoSignature => {
write!(f, "The received echo packet had invalid signature")
}
RttError::InvalidReplySignature => {
write!(f, "The received reply packet had invalid signature")
}
RttError::UnreachableNode(id, err) => {
write!(f, "Could not establish connection to {id} - {err}")
}
RttError::UnexpectedConnectionFailureWrite(id, err) => {
write!(f, "Failed to write echo packet to {id} - {err}")
}
RttError::UnexpectedConnectionFailureRead(id, err) => {
write!(f, "Failed to read reply packet from {id} - {err}")
}
RttError::ConnectionReadTimeout(id) => {
write!(f, "Timed out while trying to read reply packet from {id}")
}
RttError::ConnectionWriteTimeout(id) => {
write!(f, "Timed out while trying to write echo packet to {id}")
}
RttError::UnexpectedReplySequence => write!(
f,
"The received reply packet had an unexpected sequence number"
),
RttError::ShutdownReceived => {
write!(f, "Shutdown signal received")
}
}
}
}
impl std::error::Error for RttError {}
@@ -1,35 +0,0 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult};
use std::mem;
use time::OffsetDateTime;
pub(crate) trait VerlocStatsUpdateExt {
async fn start_new_measurements(&self, nodes_to_test: usize);
async fn append_measurement_results(&self, new_data: Vec<VerlocNodeResult>);
async fn finish_measurements(&self);
}
impl VerlocStatsUpdateExt for SharedVerlocStats {
async fn start_new_measurements(&self, nodes_to_test: usize) {
let mut guard = self.write().await;
guard.previous_run_data = mem::take(&mut guard.current_run_data);
guard.current_run_data.nodes_tested = nodes_to_test;
}
async fn append_measurement_results(&self, mut new_data: Vec<VerlocNodeResult>) {
let mut write_permit = self.write().await;
write_permit.current_run_data.results.append(&mut new_data);
// make sure the data always stays in order.
// TODO: considering the front of the results is guaranteed to be sorted, should perhaps
// a non-default sorting algorithm be used?
write_permit.current_run_data.results.sort()
}
async fn finish_measurements(&self) {
self.write().await.current_run_data.run_finished = Some(OffsetDateTime::now_utc())
}
}
-383
View File
@@ -1,383 +0,0 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::verloc::listener::PacketListener;
use crate::verloc::sender::{PacketSender, TestedNode};
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use log::*;
use nym_bin_common::version_checker::{self, parse_version};
use nym_crypto::asymmetric::identity;
use nym_network_defaults::mainnet::NYM_API;
use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult};
use nym_task::TaskClient;
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use url::Url;
use measurement::VerlocStatsUpdateExt;
// pub use crate::verloc::measurement::{AtomicVerlocResult, Verloc, VerlocResult};
pub mod error;
pub(crate) mod listener;
pub(crate) mod measurement;
pub(crate) mod packet;
pub(crate) mod sender;
// TODO: MUST BE UPDATED BEFORE ACTUAL RELEASE!!
pub const MINIMUM_NODE_VERSION: &str = "0.10.1";
// by default all of those are overwritten by config data from mixnodes directly
const DEFAULT_VERLOC_PORT: u16 = 1790;
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
const DEFAULT_BATCH_SIZE: usize = 50;
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
#[derive(Clone, Debug)]
pub struct Config {
/// Minimum semver version of a node (gateway or mixnode) that is capable of replying to echo packets.
minimum_compatible_node_version: version_checker::Version,
/// Socket address of this node on which it will be listening for the measurement packets.
listening_address: SocketAddr,
/// Specifies number of echo packets sent to each node during a measurement run.
packets_per_node: usize,
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
packet_timeout: Duration,
/// Specifies maximum amount of time to wait for the connection to get established.
connection_timeout: Duration,
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
delay_between_packets: Duration,
/// Specifies number of nodes being tested at once.
tested_nodes_batch_size: usize,
/// Specifies delay between subsequent test runs.
testing_interval: Duration,
/// Specifies delay between attempting to run the measurement again if the previous run failed
/// due to being unable to get the list of nodes.
retry_timeout: Duration,
/// URLs to the nym apis for obtaining network topology.
nym_api_urls: Vec<Url>,
}
impl Config {
pub fn build() -> ConfigBuilder {
ConfigBuilder::new()
}
}
#[must_use]
pub struct ConfigBuilder(Config);
impl ConfigBuilder {
pub fn new() -> ConfigBuilder {
Self::default()
}
pub fn minimum_compatible_node_version(mut self, version: version_checker::Version) -> Self {
self.0.minimum_compatible_node_version = version;
self
}
pub fn listening_address(mut self, listening_address: SocketAddr) -> Self {
self.0.listening_address = listening_address;
self
}
pub fn packets_per_node(mut self, packets_per_node: usize) -> Self {
self.0.packets_per_node = packets_per_node;
self
}
pub fn packet_timeout(mut self, packet_timeout: Duration) -> Self {
self.0.packet_timeout = packet_timeout;
self
}
pub fn connection_timeout(mut self, connection_timeout: Duration) -> Self {
self.0.connection_timeout = connection_timeout;
self
}
pub fn delay_between_packets(mut self, delay_between_packets: Duration) -> Self {
self.0.delay_between_packets = delay_between_packets;
self
}
pub fn tested_nodes_batch_size(mut self, tested_nodes_batch_size: usize) -> Self {
self.0.tested_nodes_batch_size = tested_nodes_batch_size;
self
}
pub fn testing_interval(mut self, testing_interval: Duration) -> Self {
self.0.testing_interval = testing_interval;
self
}
pub fn retry_timeout(mut self, retry_timeout: Duration) -> Self {
self.0.retry_timeout = retry_timeout;
self
}
pub fn nym_api_urls(mut self, nym_api_urls: Vec<Url>) -> Self {
self.0.nym_api_urls = nym_api_urls;
self
}
pub fn build(self) -> Config {
// panics here are fine as those are only ever constructed at the initial setup
assert!(
!self.0.nym_api_urls.is_empty(),
"at least one validator endpoint must be provided",
);
self.0
}
}
impl Default for ConfigBuilder {
fn default() -> Self {
ConfigBuilder(Config {
minimum_compatible_node_version: parse_version(MINIMUM_NODE_VERSION).unwrap(),
listening_address: format!("[::]:{DEFAULT_VERLOC_PORT}").parse().unwrap(),
packets_per_node: DEFAULT_PACKETS_PER_NODE,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
testing_interval: DEFAULT_TESTING_INTERVAL,
retry_timeout: DEFAULT_RETRY_TIMEOUT,
nym_api_urls: vec![NYM_API.parse().expect("Invalid default API URL")],
})
}
}
pub struct VerlocMeasurer {
config: Config,
packet_sender: Arc<PacketSender>,
packet_listener: Arc<PacketListener>,
shutdown_listener: TaskClient,
currently_used_api: usize,
// Note: this client is only fine here as it does not maintain constant connection to the validator.
// It only does bunch of REST queries. If we update it at some point to a more sophisticated (maybe signing) client,
// then it definitely cannot be constructed here and probably will need to be passed from outside,
// as mixnodes/gateways would already be using an instance of said client.
validator_client: nym_validator_client::NymApiClient,
state: SharedVerlocStats,
}
impl VerlocMeasurer {
pub fn new(
mut config: Config,
identity: Arc<identity::KeyPair>,
shutdown_listener: TaskClient,
) -> Self {
config.nym_api_urls.shuffle(&mut thread_rng());
VerlocMeasurer {
packet_sender: Arc::new(PacketSender::new(
Arc::clone(&identity),
config.packets_per_node,
config.packet_timeout,
config.connection_timeout,
config.delay_between_packets,
shutdown_listener.clone().named("VerlocPacketSender"),
)),
packet_listener: Arc::new(PacketListener::new(
config.listening_address,
Arc::clone(&identity),
shutdown_listener.clone().named("VerlocPacketListener"),
)),
shutdown_listener,
currently_used_api: 0,
validator_client: nym_validator_client::NymApiClient::new(
config.nym_api_urls[0].clone(),
),
config,
state: SharedVerlocStats::default(),
}
}
pub fn set_shared_state(&mut self, state: SharedVerlocStats) {
self.state = state;
}
fn use_next_nym_api(&mut self) {
if self.config.nym_api_urls.len() == 1 {
warn!("There's only a single validator API available - it won't be possible to use a different one");
return;
}
self.currently_used_api = (self.currently_used_api + 1) % self.config.nym_api_urls.len();
self.validator_client
.change_nym_api(self.config.nym_api_urls[self.currently_used_api].clone())
}
fn start_listening(&self) -> JoinHandle<()> {
let packet_listener = Arc::clone(&self.packet_listener);
tokio::spawn(packet_listener.run())
}
async fn perform_measurement(&self, nodes_to_test: Vec<TestedNode>) -> MeasurementOutcome {
log::trace!("Performing measurements");
if nodes_to_test.is_empty() {
log::debug!("there are no nodes to measure");
return MeasurementOutcome::Done;
}
let mut shutdown_listener = self.shutdown_listener.clone().named("VerlocMeasurement");
shutdown_listener.disarm();
for chunk in nodes_to_test.chunks(self.config.tested_nodes_batch_size) {
let mut chunk_results = Vec::with_capacity(chunk.len());
let mut measurement_chunk = chunk
.iter()
.map(|node| {
let node = *node;
let packet_sender = Arc::clone(&self.packet_sender);
// TODO: there's a potential issue here. if we make the measurement go into separate
// task, we risk biasing results with the bunch of context switches overhead
// but if we don't do it, it will take ages to complete
// TODO: check performance difference when it's not spawned as a separate task
tokio::spawn(async move {
(
packet_sender.send_packets_to_node(node).await,
node.identity,
)
})
})
.collect::<FuturesUnordered<_>>();
// exhaust the results
while !shutdown_listener.is_shutdown() {
tokio::select! {
measurement_result = measurement_chunk.next() => {
let Some(result) = measurement_result else {
// if the stream has finished, it means we got everything we could have gotten
break
};
// if we receive JoinError it means the task failed to get executed, so either there's a bigger issue with tokio
// or there was a panic inside the task itself. In either case, we should just terminate ourselves.
let execution_result = result.expect("the measurement task panicked!");
let identity = execution_result.1;
let measurement_result = match execution_result.0 {
Err(err) => {
debug!("Failed to perform measurement for {identity}: {err}");
None
}
Ok(result) => Some(result),
};
chunk_results.push(VerlocNodeResult::new(identity, measurement_result));
},
_ = shutdown_listener.recv() => {
trace!("Shutdown received while measuring");
return MeasurementOutcome::Shutdown;
}
}
}
// update the results vector with chunks as they become available (by default every 50 nodes)
self.state.append_measurement_results(chunk_results).await;
}
MeasurementOutcome::Done
}
pub async fn run(&mut self) {
self.start_listening();
while !self.shutdown_listener.is_shutdown() {
info!("Starting verloc measurements");
// TODO: should we also measure gateways?
let all_mixes = match self.validator_client.get_all_described_nodes().await {
Ok(nodes) => nodes,
Err(err) => {
error!(
"failed to obtain list of mixnodes from the validator - {}. Going to attempt to use another validator API in the next run",
err
);
self.use_next_nym_api();
sleep(self.config.retry_timeout).await;
continue;
}
};
if all_mixes.is_empty() {
warn!("There does not seem there are any nodes to measure...")
}
// we only care about address and identity
let tested_nodes = all_mixes
.into_iter()
.filter(|n| n.description.declared_role.mixnode)
.filter_map(|node| {
// try to parse the identity and host
let node_identity = node.ed25519_identity_key();
let ip = node.description.host_information.ip_address.first()?;
let verloc_port = node.description.verloc_port();
let verloc_host = SocketAddr::new(*ip, verloc_port);
// TODO: possible problem in the future, this does name resolution and theoretically
// if a lot of nodes maliciously mis-configured themselves, it might take a while to resolve them all
// However, maybe it's not a problem as if they are misconfigured, they will eventually be
// pushed out of the network and on top of that, verloc is done in separate task that runs
// only every few hours.
Some(TestedNode::new(verloc_host, node_identity))
})
.collect::<Vec<_>>();
// on start of each run remove old results
self.state.start_new_measurements(tested_nodes.len()).await;
if let MeasurementOutcome::Shutdown = self.perform_measurement(tested_nodes).await {
log::trace!("Shutting down after aborting measurements");
break;
}
// write current time to "run finished" field
self.state.finish_measurements().await;
info!(
"Finished performing verloc measurements. The next one will happen in {:?}",
self.config.testing_interval
);
tokio::select! {
_ = sleep(self.config.testing_interval) => {},
_ = self.shutdown_listener.recv() => {
log::trace!("Shutdown received while sleeping");
}
}
}
log::trace!("Verloc: Exiting");
}
}
enum MeasurementOutcome {
Done,
Shutdown,
}
@@ -324,18 +324,6 @@ pub fn unchecked_aggregate_indices_signatures(
_aggregate_indices_signatures(params, vk, signatures_shares, false) _aggregate_indices_signatures(params, vk, signatures_shares, false)
} }
/// Generates parameters for the scheme setup.
///
/// # Arguments
///
/// * `total_coins` - it is the number of coins in a freshly generated wallet. It is the public parameter of the scheme.
///
/// # Returns
///
/// A `Parameters` struct containing group parameters, public key, the number of signatures (`total_coins`),
/// and a map of signatures for each index `l`.
///
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -264,7 +264,7 @@ impl<'b> Add<&'b VerificationKeyAuth> for VerificationKeyAuth {
} }
} }
impl<'a> Mul<Scalar> for &'a VerificationKeyAuth { impl Mul<Scalar> for &VerificationKeyAuth {
type Output = VerificationKeyAuth; type Output = VerificationKeyAuth;
#[inline] #[inline]
@@ -984,7 +984,7 @@ pub struct SerialNumberRef<'a> {
pub(crate) inner: &'a [G1Projective], pub(crate) inner: &'a [G1Projective],
} }
impl<'a> SerialNumberRef<'a> { impl SerialNumberRef<'_> {
pub fn to_bytes(&self) -> Vec<u8> { pub fn to_bytes(&self) -> Vec<u8> {
let ss_len = self.inner.len(); let ss_len = self.inner.len();
let mut bytes: Vec<u8> = Vec::with_capacity(ss_len * 48); let mut bytes: Vec<u8> = Vec::with_capacity(ss_len * 48);
+2 -2
View File
@@ -206,10 +206,10 @@ impl Deref for PublicKey {
} }
} }
impl<'a, 'b> Mul<&'b Scalar> for &'a PublicKey { impl<'a> Mul<&'a Scalar> for &PublicKey {
type Output = G1Projective; type Output = G1Projective;
fn mul(self, rhs: &'b Scalar) -> Self::Output { fn mul(self, rhs: &'a Scalar) -> Self::Output {
self.0 * rhs self.0 * rhs
} }
} }
+1 -1
View File
@@ -305,7 +305,7 @@ impl<'b> Add<&'b VerificationKey> for VerificationKey {
} }
} }
impl<'a> Mul<Scalar> for &'a VerificationKey { impl Mul<Scalar> for &VerificationKey {
type Output = VerificationKey; type Output = VerificationKey;
#[inline] #[inline]
+1 -1
View File
@@ -64,7 +64,7 @@ impl<'de> Deserialize<'de> for Recipient {
{ {
struct RecipientVisitor; struct RecipientVisitor;
impl<'de> Visitor<'de> for RecipientVisitor { impl Visitor<'_> for RecipientVisitor {
type Value = Recipient; type Value = Recipient;
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
+7 -7
View File
@@ -1,6 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
//! Encoding and decoding node routing information.
//!
//! This module is responsible for encoding and decoding node routing information, so that
//! they could be later put into an appropriate field in a sphinx header.
//! Currently, that routing information is an IP address, but in principle it can be anything
//! for as long as it's going to fit in the field.
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_sphinx_types::{NodeAddressBytes, NODE_ADDRESS_LENGTH}; use nym_sphinx_types::{NodeAddressBytes, NODE_ADDRESS_LENGTH};
@@ -12,13 +19,6 @@ use thiserror::Error;
pub type NodeIdentity = identity::PublicKey; pub type NodeIdentity = identity::PublicKey;
pub const NODE_IDENTITY_SIZE: usize = identity::PUBLIC_KEY_LENGTH; pub const NODE_IDENTITY_SIZE: usize = identity::PUBLIC_KEY_LENGTH;
/// Encodoing and decoding node routing information.
///
/// This module is responsible for encoding and decoding node routing information, so that
/// they could be later put into an appropriate field in a sphinx header.
/// Currently, that routing information is an IP address, but in principle it can be anything
/// for as long as it's going to fit in the field.
/// MAX_UNPADDED_LEN represents maximum length an unpadded address could have. /// MAX_UNPADDED_LEN represents maximum length an unpadded address could have.
/// In this case it's an ipv6 socket address (with version prefix) /// In this case it's an ipv6 socket address (with version prefix)
pub const MAX_NODE_ADDRESS_UNPADDED_LEN: usize = 19; pub const MAX_NODE_ADDRESS_UNPADDED_LEN: usize = 19;
@@ -56,7 +56,7 @@ impl<'de> Deserialize<'de> for ReplySurb {
{ {
struct ReplySurbVisitor; struct ReplySurbVisitor;
impl<'de> Visitor<'de> for ReplySurbVisitor { impl Visitor<'_> for ReplySurbVisitor {
type Value = ReplySurb; type Value = ReplySurb;
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
+2 -8
View File
@@ -18,19 +18,13 @@ pub struct FramedNymPacket {
} }
impl FramedNymPacket { impl FramedNymPacket {
pub fn new(packet: NymPacket, packet_type: PacketType, use_legacy_version: bool) -> Self { pub fn new(packet: NymPacket, packet_type: PacketType) -> Self {
// If this fails somebody is using the library in a super incorrect way, because they // If this fails somebody is using the library in a super incorrect way, because they
// already managed to somehow create a sphinx packet // already managed to somehow create a sphinx packet
let packet_size = PacketSize::get_type(packet.len()).unwrap(); let packet_size = PacketSize::get_type(packet.len()).unwrap();
let use_legacy = if packet_type == PacketType::Outfox {
false
} else {
use_legacy_version
};
let header = Header { let header = Header {
packet_version: PacketVersion::new(use_legacy), packet_version: PacketVersion::new(),
packet_size, packet_size,
packet_type, packet_type,
}; };
@@ -13,12 +13,8 @@ pub enum PacketVersion {
} }
impl PacketVersion { impl PacketVersion {
pub fn new(use_legacy: bool) -> Self { pub fn new() -> Self {
if use_legacy { Self::new_versioned(CURRENT_PACKET_VERSION_NUMBER)
Self::new_legacy()
} else {
Self::new_versioned(CURRENT_PACKET_VERSION_NUMBER)
}
} }
pub fn new_legacy() -> Self { pub fn new_legacy() -> Self {
-10
View File
@@ -253,25 +253,15 @@ impl Socks5RequestContent {
/// Deserialize the request type, connection id, destination address and port, /// Deserialize the request type, connection id, destination address and port,
/// and the request body from bytes. /// and the request body from bytes.
/// ///
// TODO: this was already inaccurate
// /// Serialized bytes looks like this:
// ///
// /// --------------------------------------------------------------------------------------
// /// request_flag | connection_id | address_length | remote_address_bytes | request_data |
// /// 1 | 8 | 2 | address_length | ... |
// /// --------------------------------------------------------------------------------------
///
/// The request_flag tells us whether this is a new connection request (`new_connect`), /// The request_flag tells us whether this is a new connection request (`new_connect`),
/// an already-established connection we should send up (`new_send`), or /// an already-established connection we should send up (`new_send`), or
/// a request to close an established connection (`new_close`). /// a request to close an established connection (`new_close`).
// connect: // connect:
// RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR> // RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR>
// //
// send: // send:
// RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA // RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA
// where DATA: SEQ || TRUE_DATA // where DATA: SEQ || TRUE_DATA
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> { pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
// each request needs to at least contain flag and ConnectionId // each request needs to at least contain flag and ConnectionId
if b.is_empty() { if b.is_empty() {
+1
View File
@@ -26,3 +26,4 @@ nym-sphinx = { path = "../nymsphinx" }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
nym-metrics = { path = "../nym-metrics" } nym-metrics = { path = "../nym-metrics" }
nym-task = { path = "../task" } nym-task = { path = "../task" }
+32 -31
View File
@@ -31,40 +31,12 @@ impl GatewayStatsReporter {
/// Gateway Statistics events /// Gateway Statistics events
pub enum GatewayStatsEvent { pub enum GatewayStatsEvent {
/// Events in the lifecycle of an established client tunnel /// Events in the lifecycle of an established client tunnel
SessionStatsEvent(SessionEvent), SessionStatsEvent(GatewaySessionEvent),
}
impl GatewayStatsEvent {
/// A new session between this gateway and the client remote has successfully opened
pub fn new_session_start(client: DestinationAddressBytes) -> GatewayStatsEvent {
GatewayStatsEvent::SessionStatsEvent(SessionEvent::SessionStart {
start_time: OffsetDateTime::now_utc(),
client,
})
}
/// An existing session with the client remote has ended
pub fn new_session_stop(client: DestinationAddressBytes) -> GatewayStatsEvent {
GatewayStatsEvent::SessionStatsEvent(SessionEvent::SessionStop {
stop_time: OffsetDateTime::now_utc(),
client,
})
}
/// A new ecash ticket has been added / requested
pub fn new_ecash_ticket(
client: DestinationAddressBytes,
ticket_type: TicketType,
) -> GatewayStatsEvent {
GatewayStatsEvent::SessionStatsEvent(SessionEvent::EcashTicket {
ticket_type,
client,
})
}
} }
/// Events in the lifecycle of an established client tunnel /// Events in the lifecycle of an established client tunnel
pub enum SessionEvent { #[derive(Debug, Clone, Copy)]
pub enum GatewaySessionEvent {
/// A new session between this gateway and the client remote has successfully opened /// A new session between this gateway and the client remote has successfully opened
SessionStart { SessionStart {
/// The timestamp of the session open event /// The timestamp of the session open event
@@ -87,3 +59,32 @@ pub enum SessionEvent {
client: DestinationAddressBytes, client: DestinationAddressBytes,
}, },
} }
impl GatewaySessionEvent {
/// A new session between this gateway and the client remote has successfully opened
pub fn new_session_start(client: DestinationAddressBytes) -> GatewaySessionEvent {
GatewaySessionEvent::SessionStart {
start_time: OffsetDateTime::now_utc(),
client,
}
}
/// An existing session with the client remote has ended
pub fn new_session_stop(client: DestinationAddressBytes) -> GatewaySessionEvent {
GatewaySessionEvent::SessionStop {
stop_time: OffsetDateTime::now_utc(),
client,
}
}
/// A new ecash ticket has been added / requested
pub fn new_ecash_ticket(
client: DestinationAddressBytes,
ticket_type: TicketType,
) -> GatewaySessionEvent {
GatewaySessionEvent::EcashTicket {
ticket_type,
client,
}
}
}
+5 -1
View File
@@ -12,7 +12,7 @@
#![warn(clippy::dbg_macro)] #![warn(clippy::dbg_macro)]
use nym_crypto::asymmetric::ed25519; use nym_crypto::asymmetric::ed25519;
use sha2::Digest; use sha2::{Digest, Sha256};
/// Client specific statistics interfaces and events. /// Client specific statistics interfaces and events.
pub mod clients; pub mod clients;
@@ -36,3 +36,7 @@ fn generate_stats_id<M: AsRef<[u8]>>(prefix: &str, id_seed: M) -> String {
let output = hasher.finalize(); let output = hasher.finalize();
format!("{:x}", output) format!("{:x}", output)
} }
pub fn hash_identifier<M: AsRef<[u8]>>(identifier: M) -> String {
format!("{:x}", Sha256::digest(identifier))
}
+13 -1
View File
@@ -455,6 +455,10 @@ impl TaskClient {
self.mode.set_should_not_signal_on_drop(); self.mode.set_should_not_signal_on_drop();
} }
pub fn rearm(&mut self) {
self.mode.set_should_signal_on_drop();
}
pub fn send_we_stopped(&mut self, err: SentError) { pub fn send_we_stopped(&mut self, err: SentError) {
if self.mode.is_dummy() { if self.mode.is_dummy() {
return; return;
@@ -482,7 +486,7 @@ impl Drop for TaskClient {
if !self.mode.should_signal_on_drop() { if !self.mode.should_signal_on_drop() {
self.log( self.log(
Level::Trace, Level::Trace,
"the task client is getting dropped but inststructed to not signal: this is expected during client shutdown", "the task client is getting dropped but instructed to not signal: this is expected during client shutdown",
); );
return; return;
} else { } else {
@@ -527,6 +531,14 @@ impl ClientOperatingMode {
} }
} }
fn set_should_signal_on_drop(&mut self) {
use ClientOperatingMode::{Dummy, Listening, ListeningButDontReportHalt};
*self = match &self {
ListeningButDontReportHalt | Listening => Listening,
Dummy => Dummy,
};
}
fn set_should_not_signal_on_drop(&mut self) { fn set_should_not_signal_on_drop(&mut self) {
use ClientOperatingMode::{Dummy, Listening, ListeningButDontReportHalt}; use ClientOperatingMode::{Dummy, Listening, ListeningButDontReportHalt};
*self = match &self { *self = match &self {
+27
View File
@@ -0,0 +1,27 @@
[package]
name = "nym-verloc"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
bytes = { workspace = true }
futures = { workspace = true }
humantime = { workspace = true }
tracing = { workspace = true }
time = { workspace = true }
tokio = { workspace = true, features = ["sync", "net", "rt-multi-thread", "io-util"] }
tokio-util = { workspace = true, features = ["codec"] }
thiserror = { workspace = true }
rand = { workspace = true }
url = { workspace = true }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::io;
use std::net::SocketAddr;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum VerlocError {
#[error("the received echo packet had unexpected size")]
UnexpectedEchoPacketSize,
#[error("the received reply packet had unexpected size")]
UnexpectedReplyPacketSize,
#[error("the received echo packet had malformed sender")]
MalformedSenderIdentity,
#[error("the received echo packet had malformed signature")]
MalformedEchoSignature,
#[error("the received reply packet had malformed signature")]
MalformedReplySignature,
#[error("the received echo packet had invalid signature")]
InvalidEchoSignature,
#[error("the received reply packet had invalid signature")]
InvalidReplySignature,
#[error("could not establish connection to {identity} on {address}: {err}")]
UnreachableNode {
identity: String,
address: SocketAddr,
#[source]
err: io::Error,
},
#[error("failed to write echo packet to {identity} on {address}: {err}")]
UnexpectedConnectionFailureWrite {
identity: String,
address: SocketAddr,
#[source]
err: io::Error,
},
#[error("failed to read reply packet from {identity} on {address}: {err}")]
UnexpectedConnectionFailureRead {
identity: String,
address: SocketAddr,
#[source]
err: io::Error,
},
#[error("timed out while trying to read reply packet from {identity} on {address}")]
ConnectionReadTimeout {
identity: String,
address: SocketAddr,
},
#[error("timed out while trying to write echo packet to {identity} on {address}")]
ConnectionWriteTimeout {
identity: String,
address: SocketAddr,
},
#[error("the received reply packet had an unexpected sequence number")]
UnexpectedReplySequence,
#[error("shutdown signal received")]
ShutdownReceived,
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
#![warn(clippy::todo)]
#![warn(clippy::dbg_macro)]
pub mod error;
pub mod measurements;
pub mod models;
+135
View File
@@ -0,0 +1,135 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_validator_client::UserAgent;
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::time::Duration;
use url::Url;
// by default all of those are overwritten by config data from nym-node directly
const DEFAULT_VERLOC_PORT: u16 = 1790;
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
const DEFAULT_BATCH_SIZE: usize = 50;
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
#[derive(Clone, Debug)]
pub struct Config {
/// Socket address of this node on which it will be listening for the measurement packets.
pub listening_address: SocketAddr,
/// Specifies number of echo packets sent to each node during a measurement run.
pub packets_per_node: usize,
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
pub packet_timeout: Duration,
/// Specifies maximum amount of time to wait for the connection to get established.
pub connection_timeout: Duration,
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
pub delay_between_packets: Duration,
/// Specifies number of nodes being tested at once.
pub tested_nodes_batch_size: usize,
/// Specifies delay between subsequent test runs.
pub testing_interval: Duration,
/// Specifies delay between attempting to run the measurement again if the previous run failed
/// due to being unable to get the list of nodes.
pub retry_timeout: Duration,
/// URLs to the nym apis for obtaining network topology.
pub nym_api_urls: Vec<Url>,
/// User agent used for querying the nym-api
pub user_agent: UserAgent,
}
impl Config {
pub fn build(nym_api_urls: Vec<Url>, user_agent: impl Into<UserAgent>) -> ConfigBuilder {
ConfigBuilder::new(nym_api_urls, user_agent)
}
}
#[must_use]
pub struct ConfigBuilder(Config);
impl ConfigBuilder {
pub fn new(nym_api_urls: Vec<Url>, user_agent: impl Into<UserAgent>) -> ConfigBuilder {
ConfigBuilder(Config {
// '[::]:port'
listening_address: SocketAddr::new(
IpAddr::V6(Ipv6Addr::UNSPECIFIED),
DEFAULT_VERLOC_PORT,
),
packets_per_node: DEFAULT_PACKETS_PER_NODE,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
testing_interval: DEFAULT_TESTING_INTERVAL,
retry_timeout: DEFAULT_RETRY_TIMEOUT,
nym_api_urls,
user_agent: user_agent.into(),
})
}
pub fn listening_address(mut self, listening_address: SocketAddr) -> Self {
self.0.listening_address = listening_address;
self
}
pub fn packets_per_node(mut self, packets_per_node: usize) -> Self {
self.0.packets_per_node = packets_per_node;
self
}
pub fn packet_timeout(mut self, packet_timeout: Duration) -> Self {
self.0.packet_timeout = packet_timeout;
self
}
pub fn connection_timeout(mut self, connection_timeout: Duration) -> Self {
self.0.connection_timeout = connection_timeout;
self
}
pub fn delay_between_packets(mut self, delay_between_packets: Duration) -> Self {
self.0.delay_between_packets = delay_between_packets;
self
}
pub fn tested_nodes_batch_size(mut self, tested_nodes_batch_size: usize) -> Self {
self.0.tested_nodes_batch_size = tested_nodes_batch_size;
self
}
pub fn testing_interval(mut self, testing_interval: Duration) -> Self {
self.0.testing_interval = testing_interval;
self
}
pub fn retry_timeout(mut self, retry_timeout: Duration) -> Self {
self.0.retry_timeout = retry_timeout;
self
}
pub fn nym_api_urls(mut self, nym_api_urls: Vec<Url>) -> Self {
self.0.nym_api_urls = nym_api_urls;
self
}
pub fn build(self) -> Config {
// panics here are fine as those are only ever constructed at the initial setup
assert!(
!self.0.nym_api_urls.is_empty(),
"at least one validator endpoint must be provided",
);
self.0
}
}
@@ -1,29 +1,29 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::verloc::error::RttError; use crate::error::VerlocError;
use crate::verloc::packet::{EchoPacket, ReplyPacket}; use crate::measurements::packet::{EchoPacket, ReplyPacket};
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use futures::StreamExt; use futures::StreamExt;
use log::*;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_task::TaskClient; use nym_task::TaskClient;
use std::fmt::{Display, Formatter};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, io, process}; use std::{io, process};
use thiserror::Error;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
use tokio_util::codec::{Decoder, Encoder, Framed}; use tokio_util::codec::{Decoder, Encoder, Framed};
use tracing::{debug, error, info, trace, warn};
pub(crate) struct PacketListener { pub struct PacketListener {
address: SocketAddr, address: SocketAddr,
connection_handler: Arc<ConnectionHandler>, connection_handler: Arc<ConnectionHandler>,
shutdown: TaskClient, shutdown: TaskClient,
} }
impl PacketListener { impl PacketListener {
pub(crate) fn new( pub fn new(
address: SocketAddr, address: SocketAddr,
identity: Arc<identity::KeyPair>, identity: Arc<identity::KeyPair>,
shutdown: TaskClient, shutdown: TaskClient,
@@ -37,13 +37,13 @@ impl PacketListener {
} }
impl PacketListener { impl PacketListener {
pub(super) async fn run(self: Arc<Self>) { pub async fn run(self: Arc<Self>) {
let listener = match TcpListener::bind(self.address).await { let listener = match TcpListener::bind(self.address).await {
Ok(listener) => listener, Ok(listener) => listener,
Err(err) => { Err(err) => {
error!( error!(
"Failed to bind to {} - {}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?", "Failed to bind to {}: {err}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?",
self.address, err self.address
); );
process::exit(1); process::exit(1);
} }
@@ -71,7 +71,7 @@ impl PacketListener {
} }
}, },
_ = shutdown_listener.recv() => { _ = shutdown_listener.recv() => {
log::trace!("PacketListener: Received shutdown"); trace!("PacketListener: Received shutdown");
} }
} }
} }
@@ -137,28 +137,18 @@ impl ConnectionHandler {
} }
} }
#[derive(Debug)] #[derive(Debug, Error)]
enum EchoPacketCodecError { enum EchoPacketCodecError {
IoError(io::Error), #[error("encountered io error {0}")]
PacketRecoveryError(RttError), IoError(#[from] io::Error),
#[error("failed to correctly decode an echo packet: {0}")]
PacketRecoveryError(Box<VerlocError>),
} }
impl Display for EchoPacketCodecError { impl From<VerlocError> for EchoPacketCodecError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn from(value: VerlocError) -> Self {
match self { EchoPacketCodecError::PacketRecoveryError(Box::new(value))
EchoPacketCodecError::IoError(err) => write!(f, "encountered io error - {err}"),
EchoPacketCodecError::PacketRecoveryError(err) => {
write!(f, "failed to correctly decode an echo packet - {err}")
}
}
}
}
impl std::error::Error for EchoPacketCodecError {}
impl From<io::Error> for EchoPacketCodecError {
fn from(err: io::Error) -> Self {
EchoPacketCodecError::IoError(err)
} }
} }
@@ -188,10 +178,7 @@ impl Decoder for EchoPacketCodec {
let packet_bytes = src.split_to(EchoPacket::SIZE); let packet_bytes = src.split_to(EchoPacket::SIZE);
let echo_packet = match EchoPacket::try_from_bytes(&packet_bytes) { let echo_packet = EchoPacket::try_from_bytes(&packet_bytes)?;
Ok(packet) => packet,
Err(err) => return Err(EchoPacketCodecError::PacketRecoveryError(err)),
};
// reserve enough bytes for the next frame // reserve enough bytes for the next frame
src.reserve(EchoPacket::SIZE); src.reserve(EchoPacket::SIZE);
+225
View File
@@ -0,0 +1,225 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::measurements::metrics::SharedVerlocStats;
use crate::measurements::sender::TestedNode;
use crate::measurements::{Config, PacketListener, PacketSender};
use crate::models::VerlocNodeResult;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use nym_crypto::asymmetric::identity;
use nym_task::TaskClient;
use nym_validator_client::models::NymNodeDescription;
use nym_validator_client::NymApiClient;
use rand::prelude::SliceRandom;
use rand::thread_rng;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use tracing::{debug, error, info, trace, warn};
pub struct VerlocMeasurer {
config: Config,
packet_sender: Arc<PacketSender>,
packet_listener: Arc<PacketListener>,
shutdown_listener: TaskClient,
state: SharedVerlocStats,
}
impl VerlocMeasurer {
pub fn new(
config: Config,
identity: Arc<identity::KeyPair>,
shutdown_listener: TaskClient,
) -> Self {
VerlocMeasurer {
packet_sender: Arc::new(PacketSender::new(
Arc::clone(&identity),
config.packets_per_node,
config.packet_timeout,
config.connection_timeout,
config.delay_between_packets,
shutdown_listener.clone().named("VerlocPacketSender"),
)),
packet_listener: Arc::new(PacketListener::new(
config.listening_address,
Arc::clone(&identity),
shutdown_listener.clone().named("VerlocPacketListener"),
)),
shutdown_listener,
config,
state: SharedVerlocStats::default(),
}
}
pub fn set_shared_state(&mut self, state: SharedVerlocStats) {
self.state = state;
}
fn start_listening(&self) -> JoinHandle<()> {
let packet_listener = Arc::clone(&self.packet_listener);
tokio::spawn(packet_listener.run())
}
async fn perform_measurement(&self, nodes_to_test: Vec<TestedNode>) -> MeasurementOutcome {
trace!("Performing measurements");
if nodes_to_test.is_empty() {
debug!("there are no nodes to measure");
return MeasurementOutcome::Done;
}
let mut shutdown_listener = self.shutdown_listener.clone().named("VerlocMeasurement");
shutdown_listener.disarm();
for chunk in nodes_to_test.chunks(self.config.tested_nodes_batch_size) {
let mut chunk_results = Vec::with_capacity(chunk.len());
let mut measurement_chunk = chunk
.iter()
.map(|node| {
let node = *node;
let packet_sender = Arc::clone(&self.packet_sender);
// TODO: there's a potential issue here. if we make the measurement go into separate
// task, we risk biasing results with the bunch of context switches overhead
// but if we don't do it, it will take ages to complete
// TODO: check performance difference when it's not spawned as a separate task
tokio::spawn(async move {
(
packet_sender.send_packets_to_node(node).await,
node.identity,
)
})
})
.collect::<FuturesUnordered<_>>();
// exhaust the results
while !shutdown_listener.is_shutdown() {
tokio::select! {
measurement_result = measurement_chunk.next() => {
let Some(result) = measurement_result else {
// if the stream has finished, it means we got everything we could have gotten
break
};
// if we receive JoinError it means the task failed to get executed, so either there's a bigger issue with tokio
// or there was a panic inside the task itself. In either case, we should just terminate ourselves.
let Ok(execution_result) = result else {
error!("the verloc measurer has panicked!");
continue
};
let identity = execution_result.1;
let measurement_result = match execution_result.0 {
Err(err) => {
debug!("Failed to perform measurement for {identity}: {err}");
None
}
Ok(result) => Some(result),
};
chunk_results.push(VerlocNodeResult::new(identity, measurement_result));
},
_ = shutdown_listener.recv() => {
trace!("Shutdown received while measuring");
return MeasurementOutcome::Shutdown;
}
}
}
// update the results vector with chunks as they become available (by default every 50 nodes)
self.state.append_measurement_results(chunk_results).await;
}
MeasurementOutcome::Done
}
async fn get_list_of_nodes(&self) -> Option<Vec<NymNodeDescription>> {
let mut api_endpoints = self.config.nym_api_urls.clone();
api_endpoints.shuffle(&mut thread_rng());
for api_endpoint in api_endpoints {
let client = NymApiClient::new_with_user_agent(
api_endpoint.clone(),
self.config.user_agent.clone(),
);
match client.get_all_described_nodes().await {
Ok(res) => return Some(res),
Err(err) => {
warn!("failed to get described nodes from {api_endpoint}: {err}")
}
}
}
None
}
pub async fn run(&mut self) {
self.start_listening();
while !self.shutdown_listener.is_shutdown() {
info!("Starting verloc measurements");
// TODO: should we also measure gateways?
let Some(all_nodes) = self.get_list_of_nodes().await else {
error!("failed to obtain list of all nodes from any available api endpoint");
sleep(self.config.retry_timeout).await;
continue;
};
if all_nodes.is_empty() {
warn!("it does not seem there are any nodes to measure...");
sleep(self.config.retry_timeout).await;
continue;
}
// we only care about address and identity
let tested_nodes = all_nodes
.into_iter()
.filter_map(|node| {
// try to parse the identity and host
let node_identity = node.ed25519_identity_key();
let ip = node.description.host_information.ip_address.first()?;
let verloc_port = node.description.verloc_port();
let verloc_host = SocketAddr::new(*ip, verloc_port);
// TODO: possible problem in the future, this does name resolution and theoretically
// if a lot of nodes maliciously mis-configured themselves, it might take a while to resolve them all
// However, maybe it's not a problem as if they are misconfigured, they will eventually be
// pushed out of the network and on top of that, verloc is done in separate task that runs
// only every few hours.
Some(TestedNode::new(verloc_host, node_identity))
})
.collect::<Vec<_>>();
// on start of each run remove old results
self.state.start_new_measurements(tested_nodes.len()).await;
if let MeasurementOutcome::Shutdown = self.perform_measurement(tested_nodes).await {
trace!("Shutting down after aborting measurements");
break;
}
// write current time to "run finished" field
self.state.finish_measurements().await;
info!(
"Finished performing verloc measurements. The next one will happen in {:?}",
self.config.testing_interval
);
tokio::select! {
_ = sleep(self.config.testing_interval) => {},
_ = self.shutdown_listener.recv() => {
trace!("Shutdown received while sleeping");
}
}
}
trace!("Verloc: Exiting");
}
}
enum MeasurementOutcome {
Done,
Shutdown,
}
+50
View File
@@ -0,0 +1,50 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::models::{VerlocNodeResult, VerlocResultData};
use std::mem;
use std::sync::Arc;
use time::OffsetDateTime;
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
#[derive(Clone, Debug, Default)]
pub struct SharedVerlocStats {
inner: Arc<RwLock<VerlocStatsState>>,
}
impl SharedVerlocStats {
pub(crate) async fn start_new_measurements(&self, nodes_to_test: usize) {
let mut guard = self.write().await;
guard.previous_run_data = mem::take(&mut guard.current_run_data);
guard.current_run_data.nodes_tested = nodes_to_test;
}
pub(crate) async fn append_measurement_results(&self, mut new_data: Vec<VerlocNodeResult>) {
let mut write_permit = self.write().await;
write_permit.current_run_data.results.append(&mut new_data);
// make sure the data always stays in order.
// TODO: considering the front of the results is guaranteed to be sorted, should perhaps
// a non-default sorting algorithm be used?
write_permit.current_run_data.results.sort()
}
pub(crate) async fn finish_measurements(&self) {
self.write().await.current_run_data.run_finished = Some(OffsetDateTime::now_utc())
}
}
#[derive(Clone, Debug, Default)]
pub struct VerlocStatsState {
pub current_run_data: VerlocResultData,
pub previous_run_data: VerlocResultData,
}
impl SharedVerlocStats {
pub async fn read(&self) -> RwLockReadGuard<'_, VerlocStatsState> {
self.inner.read().await
}
pub async fn write(&self) -> RwLockWriteGuard<'_, VerlocStatsState> {
self.inner.write().await
}
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod config;
pub mod listener;
pub mod measurer;
pub mod metrics;
pub mod packet;
pub mod sender;
pub use config::{Config, ConfigBuilder};
pub use listener::PacketListener;
pub use measurer::VerlocMeasurer;
pub use metrics::{SharedVerlocStats, VerlocStatsState};
pub use packet::{EchoPacket, ReplyPacket};
pub use sender::PacketSender;
@@ -1,20 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::verloc::error::RttError; use crate::error::VerlocError;
use nym_crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH}; use nym_crypto::asymmetric::ed25519::{self, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH};
pub(crate) struct EchoPacket { pub struct EchoPacket {
sequence_number: u64, sequence_number: u64,
sender: identity::PublicKey, sender: ed25519::PublicKey,
signature: identity::Signature, signature: ed25519::Signature,
} }
impl EchoPacket { impl EchoPacket {
pub(crate) const SIZE: usize = 8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH; pub(crate) const SIZE: usize = 8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH;
pub(crate) fn new(sequence_number: u64, keys: &identity::KeyPair) -> Self { pub(crate) fn new(sequence_number: u64, keys: &ed25519::KeyPair) -> Self {
let bytes_to_sign = sequence_number let bytes_to_sign = sequence_number
.to_be_bytes() .to_be_bytes()
.iter() .iter()
@@ -42,20 +42,22 @@ impl EchoPacket {
.collect() .collect()
} }
pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<Self, RttError> { pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<Self, VerlocError> {
if bytes.len() != Self::SIZE { if bytes.len() != Self::SIZE {
return Err(RttError::UnexpectedEchoPacketSize); return Err(VerlocError::UnexpectedEchoPacketSize);
} }
// SAFETY: we have ensured our packet has correct size
#[allow(clippy::unwrap_used)]
let sequence_number = u64::from_be_bytes(bytes[..8].try_into().unwrap()); let sequence_number = u64::from_be_bytes(bytes[..8].try_into().unwrap());
let sender = identity::PublicKey::from_bytes(&bytes[8..8 + PUBLIC_KEY_LENGTH]) let sender = ed25519::PublicKey::from_bytes(&bytes[8..8 + PUBLIC_KEY_LENGTH])
.map_err(|_| RttError::MalformedSenderIdentity)?; .map_err(|_| VerlocError::MalformedSenderIdentity)?;
let signature = identity::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH..]) let signature = ed25519::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH..])
.map_err(|_| RttError::MalformedEchoSignature)?; .map_err(|_| VerlocError::MalformedEchoSignature)?;
sender sender
.verify(&bytes[..Self::SIZE - SIGNATURE_LENGTH], &signature) .verify(&bytes[..Self::SIZE - SIGNATURE_LENGTH], &signature)
.map_err(|_| RttError::InvalidEchoSignature)?; .map_err(|_| VerlocError::InvalidEchoSignature)?;
Ok(EchoPacket { Ok(EchoPacket {
sequence_number, sequence_number,
@@ -64,7 +66,7 @@ impl EchoPacket {
}) })
} }
pub(crate) fn construct_reply(self, private_key: &identity::PrivateKey) -> ReplyPacket { pub(crate) fn construct_reply(self, private_key: &ed25519::PrivateKey) -> ReplyPacket {
let bytes = self.to_bytes(); let bytes = self.to_bytes();
let signature = private_key.sign(bytes); let signature = private_key.sign(bytes);
ReplyPacket { ReplyPacket {
@@ -74,9 +76,9 @@ impl EchoPacket {
} }
} }
pub(crate) struct ReplyPacket { pub struct ReplyPacket {
base_packet: EchoPacket, base_packet: EchoPacket,
signature: identity::Signature, signature: ed25519::Signature,
} }
impl ReplyPacket { impl ReplyPacket {
@@ -96,21 +98,21 @@ impl ReplyPacket {
pub(crate) fn try_from_bytes( pub(crate) fn try_from_bytes(
bytes: &[u8], bytes: &[u8],
remote_identity: &identity::PublicKey, remote_ed25519: &ed25519::PublicKey,
) -> Result<Self, RttError> { ) -> Result<Self, VerlocError> {
if bytes.len() != Self::SIZE { if bytes.len() != Self::SIZE {
return Err(RttError::UnexpectedReplyPacketSize); return Err(VerlocError::UnexpectedReplyPacketSize);
} }
let base_packet = let base_packet =
EchoPacket::try_from_bytes(&bytes[..8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH])?; EchoPacket::try_from_bytes(&bytes[..8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH])?;
let signature = let signature =
identity::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH..]) ed25519::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH..])
.map_err(|_| RttError::MalformedReplySignature)?; .map_err(|_| VerlocError::MalformedReplySignature)?;
remote_identity remote_ed25519
.verify(&bytes[..Self::SIZE - SIGNATURE_LENGTH], &signature) .verify(&bytes[..Self::SIZE - SIGNATURE_LENGTH], &signature)
.map_err(|_| RttError::InvalidReplySignature)?; .map_err(|_| VerlocError::InvalidReplySignature)?;
Ok(ReplyPacket { Ok(ReplyPacket {
base_packet, base_packet,
@@ -1,11 +1,10 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::verloc::error::RttError; use crate::error::VerlocError;
use crate::verloc::packet::{EchoPacket, ReplyPacket}; use crate::measurements::packet::{EchoPacket, ReplyPacket};
use log::*; use crate::models::VerlocMeasurement;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::ed25519;
use nym_node_http_api::state::metrics::VerlocMeasurement;
use nym_task::TaskClient; use nym_task::TaskClient;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use std::net::SocketAddr; use std::net::SocketAddr;
@@ -15,15 +14,16 @@ use std::{fmt, io};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::time::sleep; use tokio::time::sleep;
use tracing::{debug, trace};
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub(crate) struct TestedNode { pub(crate) struct TestedNode {
pub(crate) address: SocketAddr, pub(crate) address: SocketAddr,
pub(crate) identity: identity::PublicKey, pub(crate) identity: ed25519::PublicKey,
} }
impl TestedNode { impl TestedNode {
pub(crate) fn new(address: SocketAddr, identity: identity::PublicKey) -> Self { pub(crate) fn new(address: SocketAddr, identity: ed25519::PublicKey) -> Self {
TestedNode { address, identity } TestedNode { address, identity }
} }
} }
@@ -38,8 +38,8 @@ impl fmt::Display for TestedNode {
} }
} }
pub(crate) struct PacketSender { pub struct PacketSender {
identity: Arc<identity::KeyPair>, identity: Arc<ed25519::KeyPair>,
// timeout for receiving before sending new one // timeout for receiving before sending new one
packets_per_node: usize, packets_per_node: usize,
packet_timeout: Duration, packet_timeout: Duration,
@@ -49,8 +49,8 @@ pub(crate) struct PacketSender {
} }
impl PacketSender { impl PacketSender {
pub(super) fn new( pub fn new(
identity: Arc<identity::KeyPair>, identity: Arc<ed25519::KeyPair>,
packets_per_node: usize, packets_per_node: usize,
packet_timeout: Duration, packet_timeout: Duration,
connection_timeout: Duration, connection_timeout: Duration,
@@ -82,7 +82,7 @@ impl PacketSender {
pub(super) async fn send_packets_to_node( pub(super) async fn send_packets_to_node(
self: Arc<Self>, self: Arc<Self>,
tested_node: TestedNode, tested_node: TestedNode,
) -> Result<VerlocMeasurement, RttError> { ) -> Result<VerlocMeasurement, VerlocError> {
let mut shutdown_listener = self.shutdown_listener.fork(tested_node.address.to_string()); let mut shutdown_listener = self.shutdown_listener.fork(tested_node.address.to_string());
shutdown_listener.disarm(); shutdown_listener.disarm();
@@ -93,16 +93,18 @@ impl PacketSender {
.await .await
{ {
Err(_timeout) => { Err(_timeout) => {
return Err(RttError::UnreachableNode( return Err(VerlocError::UnreachableNode {
tested_node.identity.to_base58_string(), identity: tested_node.identity.to_string(),
io::ErrorKind::TimedOut.into(), err: io::ErrorKind::TimedOut.into(),
)) address: tested_node.address,
})
} }
Ok(Err(err)) => { Ok(Err(err)) => {
return Err(RttError::UnreachableNode( return Err(VerlocError::UnreachableNode {
tested_node.identity.to_base58_string(), identity: tested_node.identity.to_string(),
err, err,
)) address: tested_node.address,
})
} }
Ok(Ok(conn)) => conn, Ok(Ok(conn)) => conn,
}; };
@@ -121,33 +123,34 @@ impl PacketSender {
write = tokio::time::timeout(self.packet_timeout, conn.write_all(packet_bytes.as_ref())) => { write = tokio::time::timeout(self.packet_timeout, conn.write_all(packet_bytes.as_ref())) => {
match write { match write {
Err(_timeout) => { Err(_timeout) => {
let identity_string = tested_node.identity.to_base58_string(); let identity = tested_node.identity;
debug!( debug!(
"failed to write echo packet to {} within {:?}. Stopping the test.", "failed to write echo packet to {identity} within {:?}. Stopping the test.",
identity_string, self.packet_timeout self.packet_timeout
); );
return Err(RttError::UnexpectedConnectionFailureWrite( return Err(VerlocError::UnexpectedConnectionFailureWrite{
identity_string, identity: identity.to_string(),
io::ErrorKind::TimedOut.into(), err:io::ErrorKind::TimedOut.into(),
)); address: tested_node.address
});
} }
Ok(Err(err)) => { Ok(Err(err)) => {
let identity_string = tested_node.identity.to_base58_string(); let identity = tested_node.identity;
debug!( debug!(
"failed to write echo packet to {} - {}. Stopping the test.", "failed to write echo packet to {identity}: {err}. Stopping the test.",
identity_string, err
); );
return Err(RttError::UnexpectedConnectionFailureWrite( return Err(VerlocError::UnexpectedConnectionFailureWrite{
identity_string, identity: identity.to_string(),
err, err,
)); address: tested_node.address
});
} }
Ok(Ok(_)) => {} Ok(Ok(_)) => {}
} }
}, },
_ = shutdown_listener.recv() => { _ = shutdown_listener.recv() => {
log::trace!("PacketSender: Received shutdown while sending"); trace!("PacketSender: Received shutdown while sending");
return Err(RttError::ShutdownReceived); return Err(VerlocError::ShutdownReceived);
}, },
} }
@@ -156,15 +159,15 @@ impl PacketSender {
let reply_packet_future = async { let reply_packet_future = async {
let mut buf = [0u8; ReplyPacket::SIZE]; let mut buf = [0u8; ReplyPacket::SIZE];
if let Err(err) = conn.read_exact(&mut buf).await { if let Err(err) = conn.read_exact(&mut buf).await {
let identity = tested_node.identity;
debug!( debug!(
"failed to read reply packet from {} - {}. Stopping the test.", "failed to read reply packet from {identity}: {err}. Stopping the test.",
tested_node.identity.to_base58_string(),
err
); );
return Err(RttError::UnexpectedConnectionFailureRead( return Err(VerlocError::UnexpectedConnectionFailureRead {
tested_node.identity.to_base58_string(), identity: identity.to_string(),
err, err,
)); address: tested_node.address,
});
} }
ReplyPacket::try_from_bytes(&buf, &tested_node.identity) ReplyPacket::try_from_bytes(&buf, &tested_node.identity)
}; };
@@ -180,15 +183,16 @@ impl PacketSender {
"failed to receive reply to our echo packet within {:?}. Stopping the test", "failed to receive reply to our echo packet within {:?}. Stopping the test",
self.packet_timeout self.packet_timeout
); );
return Err(RttError::ConnectionReadTimeout( return Err(VerlocError::ConnectionReadTimeout{
tested_node.identity.to_base58_string(), identity: tested_node.identity.to_string(),
)); address: tested_node.address
});
} }
} }
}, },
_ = shutdown_listener.recv() => { _ = shutdown_listener.recv() => {
log::trace!("PacketSender: Received shutdown while waiting for reply"); trace!("PacketSender: Received shutdown while waiting for reply");
return Err(RttError::ShutdownReceived); return Err(VerlocError::ShutdownReceived);
} }
}; };
@@ -198,7 +202,7 @@ impl PacketSender {
// we have received the previous one // we have received the previous one
if reply_packet.base_sequence_number() != seq { if reply_packet.base_sequence_number() != seq {
debug!("Received reply packet with invalid sequence number! Got {} expected {}. Stopping the test", reply_packet.base_sequence_number(), seq); debug!("Received reply packet with invalid sequence number! Got {} expected {}. Stopping the test", reply_packet.base_sequence_number(), seq);
return Err(RttError::UnexpectedReplySequence); return Err(VerlocError::UnexpectedReplySequence);
} }
let time_taken = tokio::time::Instant::now().duration_since(start); let time_taken = tokio::time::Instant::now().duration_since(start);
+225
View File
@@ -0,0 +1,225 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_crypto::asymmetric::ed25519::{self};
use std::cmp::Ordering;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::time::Duration;
use time::OffsetDateTime;
#[derive(Debug, Clone)]
pub struct VerlocResultData {
pub nodes_tested: usize,
pub run_started: OffsetDateTime,
pub run_finished: Option<OffsetDateTime>,
pub results: Vec<VerlocNodeResult>,
}
impl Default for VerlocResultData {
fn default() -> Self {
VerlocResultData {
nodes_tested: 0,
run_started: OffsetDateTime::now_utc(),
run_finished: None,
results: vec![],
}
}
}
impl VerlocResultData {
pub fn run_finished(&self) -> bool {
self.run_finished.is_some()
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct VerlocNodeResult {
pub node_identity: ed25519::PublicKey,
pub latest_measurement: Option<VerlocMeasurement>,
}
impl VerlocNodeResult {
pub fn new(
node_identity: ed25519::PublicKey,
latest_measurement: Option<VerlocMeasurement>,
) -> Self {
VerlocNodeResult {
node_identity,
latest_measurement,
}
}
}
impl PartialOrd for VerlocNodeResult {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VerlocNodeResult {
fn cmp(&self, other: &Self) -> Ordering {
// if both have measurement, compare measurements
// then if only one have measurement, prefer that one
// completely ignore identity as it makes no sense to order by it
if let Some(self_measurement) = &self.latest_measurement {
if let Some(other_measurement) = &other.latest_measurement {
self_measurement.cmp(other_measurement)
} else {
Ordering::Less
}
} else if other.latest_measurement.is_some() {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct VerlocMeasurement {
/// Minimum RTT duration it took to receive an echo packet.
pub minimum: Duration,
/// Average RTT duration it took to receive the echo packets.
pub mean: Duration,
/// Maximum RTT duration it took to receive an echo packet.
pub maximum: Duration,
/// The standard deviation of the RTT duration it took to receive the echo packets.
pub standard_deviation: Duration,
}
impl VerlocMeasurement {
pub fn new(raw_results: &[Duration]) -> Self {
let minimum = raw_results.iter().min().copied().unwrap_or_default();
let maximum = raw_results.iter().max().copied().unwrap_or_default();
let mean = Self::duration_mean(raw_results);
let standard_deviation = Self::duration_standard_deviation(raw_results, mean);
VerlocMeasurement {
minimum,
mean,
maximum,
standard_deviation,
}
}
fn duration_mean(data: &[Duration]) -> Duration {
if data.is_empty() {
return Default::default();
}
let sum = data.iter().sum::<Duration>();
let count = data.len() as u32;
sum / count
}
fn duration_standard_deviation(data: &[Duration], mean: Duration) -> Duration {
if data.is_empty() {
return Default::default();
}
let variance_micros = data
.iter()
.map(|&value| {
// make sure we don't underflow
let diff = if mean > value {
mean - value
} else {
value - mean
};
// we don't need nanos precision
let diff_micros = diff.as_micros();
diff_micros * diff_micros
})
.sum::<u128>()
/ data.len() as u128;
// we shouldn't really overflow as our differences shouldn't be larger than couple seconds at the worst possible case scenario
let std_deviation_micros = (variance_micros as f64).sqrt() as u64;
Duration::from_micros(std_deviation_micros)
}
}
impl Display for VerlocMeasurement {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"rtt min/avg/max/mdev = {} / {} / {} / {}",
humantime::format_duration(self.minimum),
humantime::format_duration(self.mean),
humantime::format_duration(self.maximum),
humantime::format_duration(self.standard_deviation)
)
}
}
impl PartialOrd for VerlocMeasurement {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VerlocMeasurement {
fn cmp(&self, other: &Self) -> Ordering {
// minimum value is most important, then look at standard deviation, then mean and finally maximum
let min_cmp = self.minimum.cmp(&other.minimum);
if min_cmp != Ordering::Equal {
return min_cmp;
}
let std_dev_cmp = self.standard_deviation.cmp(&other.standard_deviation);
if std_dev_cmp != Ordering::Equal {
return std_dev_cmp;
}
let std_dev_cmp = self.mean.cmp(&other.mean);
if std_dev_cmp != Ordering::Equal {
return std_dev_cmp;
}
self.maximum.cmp(&other.maximum)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sorting_vec_of_verlocs() {
let some_identity =
ed25519::PublicKey::from_base58_string("Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt")
.unwrap();
let no_measurement = VerlocNodeResult::new(some_identity, None);
let low_min = VerlocNodeResult::new(
some_identity,
Some(VerlocMeasurement {
minimum: Duration::from_millis(42),
mean: Duration::from_millis(43),
maximum: Duration::from_millis(44),
standard_deviation: Duration::from_millis(45),
}),
);
let higher_min = VerlocNodeResult::new(
some_identity,
Some(VerlocMeasurement {
minimum: Duration::from_millis(420),
mean: Duration::from_millis(430),
maximum: Duration::from_millis(440),
standard_deviation: Duration::from_millis(450),
}),
);
let mut vec_verloc = vec![no_measurement, low_min, no_measurement, higher_min];
vec_verloc.sort();
let expected_sorted = vec![low_min, higher_min, no_measurement, no_measurement];
assert_eq!(expected_sorted, vec_verloc);
}
}
+2
View File
@@ -26,6 +26,8 @@ log.workspace = true
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] } tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
tokio-stream = { workspace = true } tokio-stream = { workspace = true }
time = { workspace = true }
tracing = { workspace = true }
nym-authenticator-requests = { path = "../authenticator-requests" } nym-authenticator-requests = { path = "../authenticator-requests" }
nym-credential-verification = { path = "../credential-verification" } nym-credential-verification = { path = "../credential-verification" }
+1 -1
View File
@@ -16,7 +16,7 @@ pub enum Error {
MissingClientBandwidthEntry, MissingClientBandwidthEntry,
#[error("{0}")] #[error("{0}")]
GatewayStorage(#[from] nym_gateway_storage::error::StorageError), GatewayStorage(#[from] nym_gateway_storage::error::GatewayStorageError),
#[error("{0}")] #[error("{0}")]
SystemTime(#[from] std::time::SystemTimeError), SystemTime(#[from] std::time::SystemTimeError),
+17 -7
View File
@@ -7,19 +7,22 @@
// #![warn(clippy::unwrap_used)] // #![warn(clippy::unwrap_used)]
use defguard_wireguard_rs::WGApi; use defguard_wireguard_rs::WGApi;
#[cfg(target_os = "linux")]
use defguard_wireguard_rs::{host::Peer, key::Key, net::IpAddrMask};
use nym_crypto::asymmetric::encryption::KeyPair; use nym_crypto::asymmetric::encryption::KeyPair;
#[cfg(target_os = "linux")]
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
use nym_wireguard_types::Config; use nym_wireguard_types::Config;
use peer_controller::PeerControlRequest; use peer_controller::PeerControlRequest;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::{self, Receiver, Sender}; use tokio::sync::mpsc::{self, Receiver, Sender};
#[cfg(target_os = "linux")]
use defguard_wireguard_rs::{host::Peer, key::Key, net::IpAddrMask};
#[cfg(target_os = "linux")]
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
pub(crate) mod error; pub(crate) mod error;
pub mod peer_controller; pub mod peer_controller;
pub mod peer_handle; pub mod peer_handle;
pub mod peer_storage_manager;
pub struct WgApiWrapper { pub struct WgApiWrapper {
inner: WGApi, inner: WGApi,
@@ -80,8 +83,8 @@ pub struct WireguardData {
/// Start wireguard device /// Start wireguard device
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>( pub async fn start_wireguard(
storage: St, storage: nym_gateway_storage::GatewayStorage,
all_peers: Vec<nym_gateway_storage::models::WireguardPeer>, all_peers: Vec<nym_gateway_storage::models::WireguardPeer>,
task_client: nym_task::TaskClient, task_client: nym_task::TaskClient,
wireguard_data: WireguardData, wireguard_data: WireguardData,
@@ -92,6 +95,7 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
use peer_controller::PeerController; use peer_controller::PeerController;
use std::collections::HashMap; use std::collections::HashMap;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tracing::info;
let ifname = String::from(WG_TUN_BASE_NAME); let ifname = String::from(WG_TUN_BASE_NAME);
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), false)?; let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), false)?;
@@ -118,8 +122,9 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
storage storage
.insert_wireguard_peer(peer, bandwidth_manager.is_some()) .insert_wireguard_peer(peer, bandwidth_manager.is_some())
.await?; .await?;
peer_bandwidth_managers.insert(peer.public_key.clone(), bandwidth_manager); peer_bandwidth_managers.insert(peer.public_key.clone(), (bandwidth_manager, peer.clone()));
} }
wg_api.create_interface()?; wg_api.create_interface()?;
let interface_config = InterfaceConfiguration { let interface_config = InterfaceConfiguration {
name: ifname.clone(), name: ifname.clone(),
@@ -129,6 +134,11 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
peers, peers,
mtu: None, mtu: None,
}; };
info!(
"attempting to configure wireguard interface '{ifname}': address={}, port={}",
interface_config.address, interface_config.port
);
wg_api.configure_interface(&interface_config)?; wg_api.configure_interface(&interface_config)?;
std::process::Command::new("ip") std::process::Command::new("ip")
.args([ .args([
+29 -13
View File
@@ -7,6 +7,7 @@ use defguard_wireguard_rs::{
WireguardInterfaceApi, WireguardInterfaceApi,
}; };
use futures::channel::oneshot; use futures::channel::oneshot;
use log::info;
use nym_authenticator_requests::latest::registration::{ use nym_authenticator_requests::latest::registration::{
RemainingBandwidthData, BANDWIDTH_CAP_PER_DAY, RemainingBandwidthData, BANDWIDTH_CAP_PER_DAY,
}; };
@@ -14,15 +15,15 @@ use nym_credential_verification::{
bandwidth_storage_manager::BandwidthStorageManager, BandwidthFlushingBehaviourConfig, bandwidth_storage_manager::BandwidthStorageManager, BandwidthFlushingBehaviourConfig,
ClientBandwidth, ClientBandwidth,
}; };
use nym_gateway_storage::Storage; use nym_gateway_storage::GatewayStorage;
use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK; use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use tokio::sync::{mpsc, RwLock}; use tokio::sync::{mpsc, RwLock};
use tokio_stream::{wrappers::IntervalStream, StreamExt}; use tokio_stream::{wrappers::IntervalStream, StreamExt};
use crate::peer_handle::PeerHandle;
use crate::WgApiWrapper; use crate::WgApiWrapper;
use crate::{error::Error, peer_handle::SharedBandwidthStorageManager}; use crate::{error::Error, peer_handle::SharedBandwidthStorageManager};
use crate::{peer_handle::PeerHandle, peer_storage_manager::PeerStorageManager};
pub enum PeerControlRequest { pub enum PeerControlRequest {
AddPeer { AddPeer {
@@ -62,24 +63,24 @@ pub struct QueryBandwidthControlResponse {
pub bandwidth_data: Option<RemainingBandwidthData>, pub bandwidth_data: Option<RemainingBandwidthData>,
} }
pub struct PeerController<St: Storage + Clone + 'static> { pub struct PeerController {
storage: St, storage: GatewayStorage,
// used to receive commands from individual handles too // used to receive commands from individual handles too
request_tx: mpsc::Sender<PeerControlRequest>, request_tx: mpsc::Sender<PeerControlRequest>,
request_rx: mpsc::Receiver<PeerControlRequest>, request_rx: mpsc::Receiver<PeerControlRequest>,
wg_api: Arc<WgApiWrapper>, wg_api: Arc<WgApiWrapper>,
host_information: Arc<RwLock<Host>>, host_information: Arc<RwLock<Host>>,
bw_storage_managers: HashMap<Key, Option<SharedBandwidthStorageManager<St>>>, bw_storage_managers: HashMap<Key, Option<SharedBandwidthStorageManager>>,
timeout_check_interval: IntervalStream, timeout_check_interval: IntervalStream,
task_client: nym_task::TaskClient, task_client: nym_task::TaskClient,
} }
impl<St: Storage + Clone + 'static> PeerController<St> { impl PeerController {
pub fn new( pub fn new(
storage: St, storage: GatewayStorage,
wg_api: Arc<WgApiWrapper>, wg_api: Arc<WgApiWrapper>,
initial_host_information: Host, initial_host_information: Host,
bw_storage_managers: HashMap<Key, Option<SharedBandwidthStorageManager<St>>>, bw_storage_managers: HashMap<Key, (Option<SharedBandwidthStorageManager>, Peer)>,
request_tx: mpsc::Sender<PeerControlRequest>, request_tx: mpsc::Sender<PeerControlRequest>,
request_rx: mpsc::Receiver<PeerControlRequest>, request_rx: mpsc::Receiver<PeerControlRequest>,
task_client: nym_task::TaskClient, task_client: nym_task::TaskClient,
@@ -88,11 +89,16 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
tokio::time::interval(DEFAULT_PEER_TIMEOUT_CHECK), tokio::time::interval(DEFAULT_PEER_TIMEOUT_CHECK),
); );
let host_information = Arc::new(RwLock::new(initial_host_information)); let host_information = Arc::new(RwLock::new(initial_host_information));
for (public_key, bandwidth_storage_manager) in bw_storage_managers.iter() { for (public_key, (bandwidth_storage_manager, peer)) in bw_storage_managers.iter() {
let mut handle = PeerHandle::new( let peer_storage_manager = PeerStorageManager::new(
storage.clone(), storage.clone(),
peer.clone(),
bandwidth_storage_manager.is_some(),
);
let mut handle = PeerHandle::new(
public_key.clone(), public_key.clone(),
host_information.clone(), host_information.clone(),
peer_storage_manager,
bandwidth_storage_manager.clone(), bandwidth_storage_manager.clone(),
request_tx.clone(), request_tx.clone(),
&task_client, &task_client,
@@ -103,6 +109,10 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
} }
}); });
} }
let bw_storage_managers = bw_storage_managers
.into_iter()
.map(|(k, (m, _))| (k, m))
.collect();
PeerController { PeerController {
storage, storage,
@@ -149,9 +159,9 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
} }
pub async fn generate_bandwidth_manager( pub async fn generate_bandwidth_manager(
storage: St, storage: GatewayStorage,
public_key: &Key, public_key: &Key,
) -> Result<Option<BandwidthStorageManager<St>>, Error> { ) -> Result<Option<BandwidthStorageManager>, Error> {
if let Some(client_id) = storage if let Some(client_id) = storage
.get_wireguard_peer(&public_key.to_string()) .get_wireguard_peer(&public_key.to_string())
.await? .await?
@@ -184,10 +194,15 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
Self::generate_bandwidth_manager(self.storage.clone(), &peer.public_key) Self::generate_bandwidth_manager(self.storage.clone(), &peer.public_key)
.await? .await?
.map(|bw_m| Arc::new(RwLock::new(bw_m))); .map(|bw_m| Arc::new(RwLock::new(bw_m)));
let mut handle = PeerHandle::new( let peer_storage_manager = PeerStorageManager::new(
self.storage.clone(), self.storage.clone(),
peer.clone(),
bandwidth_storage_manager.is_some(),
);
let mut handle = PeerHandle::new(
peer.public_key.clone(), peer.public_key.clone(),
self.host_information.clone(), self.host_information.clone(),
peer_storage_manager,
bandwidth_storage_manager.clone(), bandwidth_storage_manager.clone(),
self.request_tx.clone(), self.request_tx.clone(),
&self.task_client, &self.task_client,
@@ -243,6 +258,7 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
} }
pub async fn run(&mut self) { pub async fn run(&mut self) {
info!("started wireguard peer controller");
loop { loop {
tokio::select! { tokio::select! {
_ = self.timeout_check_interval.next() => { _ = self.timeout_check_interval.next() => {
+25 -16
View File
@@ -3,13 +3,13 @@
use crate::error::Error; use crate::error::Error;
use crate::peer_controller::PeerControlRequest; use crate::peer_controller::PeerControlRequest;
use crate::peer_storage_manager::PeerStorageManager;
use defguard_wireguard_rs::host::Peer; use defguard_wireguard_rs::host::Peer;
use defguard_wireguard_rs::{host::Host, key::Key}; use defguard_wireguard_rs::{host::Host, key::Key};
use futures::channel::oneshot; use futures::channel::oneshot;
use nym_authenticator_requests::latest::registration::BANDWIDTH_CAP_PER_DAY; use nym_authenticator_requests::latest::registration::BANDWIDTH_CAP_PER_DAY;
use nym_credential_verification::bandwidth_storage_manager::BandwidthStorageManager; use nym_credential_verification::bandwidth_storage_manager::BandwidthStorageManager;
use nym_gateway_storage::models::WireguardPeer; use nym_gateway_storage::models::WireguardPeer;
use nym_gateway_storage::Storage;
use nym_task::TaskClient; use nym_task::TaskClient;
use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK; use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK;
use std::sync::Arc; use std::sync::Arc;
@@ -17,26 +17,26 @@ use std::time::{Duration, SystemTime};
use tokio::sync::{mpsc, RwLock}; use tokio::sync::{mpsc, RwLock};
use tokio_stream::{wrappers::IntervalStream, StreamExt}; use tokio_stream::{wrappers::IntervalStream, StreamExt};
pub(crate) type SharedBandwidthStorageManager<St> = Arc<RwLock<BandwidthStorageManager<St>>>; pub(crate) type SharedBandwidthStorageManager = Arc<RwLock<BandwidthStorageManager>>;
const AUTO_REMOVE_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 30); // 30 days const AUTO_REMOVE_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 30); // 30 days
pub struct PeerHandle<St> { pub struct PeerHandle {
storage: St,
public_key: Key, public_key: Key,
host_information: Arc<RwLock<Host>>, host_information: Arc<RwLock<Host>>,
bandwidth_storage_manager: Option<SharedBandwidthStorageManager<St>>, peer_storage_manager: PeerStorageManager,
bandwidth_storage_manager: Option<SharedBandwidthStorageManager>,
request_tx: mpsc::Sender<PeerControlRequest>, request_tx: mpsc::Sender<PeerControlRequest>,
timeout_check_interval: IntervalStream, timeout_check_interval: IntervalStream,
task_client: TaskClient, task_client: TaskClient,
startup_timestamp: SystemTime, startup_timestamp: SystemTime,
} }
impl<St: Storage + Clone + 'static> PeerHandle<St> { impl PeerHandle {
pub fn new( pub fn new(
storage: St,
public_key: Key, public_key: Key,
host_information: Arc<RwLock<Host>>, host_information: Arc<RwLock<Host>>,
bandwidth_storage_manager: Option<SharedBandwidthStorageManager<St>>, peer_storage_manager: PeerStorageManager,
bandwidth_storage_manager: Option<SharedBandwidthStorageManager>,
request_tx: mpsc::Sender<PeerControlRequest>, request_tx: mpsc::Sender<PeerControlRequest>,
task_client: &TaskClient, task_client: &TaskClient,
) -> Self { ) -> Self {
@@ -46,9 +46,9 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
let mut task_client = task_client.fork(format!("peer-{public_key}")); let mut task_client = task_client.fork(format!("peer-{public_key}"));
task_client.disarm(); task_client.disarm();
PeerHandle { PeerHandle {
storage,
public_key, public_key,
host_information, host_information,
peer_storage_manager,
bandwidth_storage_manager, bandwidth_storage_manager,
request_tx, request_tx,
timeout_check_interval, timeout_check_interval,
@@ -84,16 +84,19 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
.ok_or(Error::InconsistentConsumedBytes)? .ok_or(Error::InconsistentConsumedBytes)?
.try_into() .try_into()
.map_err(|_| Error::InconsistentConsumedBytes)?; .map_err(|_| Error::InconsistentConsumedBytes)?;
if spent_bandwidth > 0 if spent_bandwidth > 0 {
&& bandwidth_manager self.peer_storage_manager.update_trx(kernel_peer);
if bandwidth_manager
.write() .write()
.await .await
.try_use_bandwidth(spent_bandwidth) .try_use_bandwidth(spent_bandwidth)
.await .await
.is_err() .is_err()
{ {
let success = self.remove_peer().await?; let success = self.remove_peer().await?;
return Ok(!success); self.peer_storage_manager.remove_peer();
return Ok(!success);
}
} }
} else { } else {
if SystemTime::now().duration_since(self.startup_timestamp)? >= AUTO_REMOVE_AFTER { if SystemTime::now().duration_since(self.startup_timestamp)? >= AUTO_REMOVE_AFTER {
@@ -132,7 +135,7 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
// the host information hasn't beed updated yet // the host information hasn't beed updated yet
continue; continue;
}; };
let Some(storage_peer) = self.storage.get_wireguard_peer(&self.public_key.to_string()).await? else { let Some(storage_peer) = self.peer_storage_manager.get_peer() else {
log::debug!("Peer {:?} not in storage anymore, shutting down handle", self.public_key); log::debug!("Peer {:?} not in storage anymore, shutting down handle", self.public_key);
return Ok(()); return Ok(());
}; };
@@ -141,12 +144,18 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
return Ok(()); return Ok(());
} else { } else {
// Update storage values // Update storage values
self.storage.insert_wireguard_peer(&kernel_peer, self.bandwidth_storage_manager.is_some()).await?; self.peer_storage_manager.sync_storage_peer().await?;
} }
} }
_ = self.task_client.recv() => { _ = self.task_client.recv() => {
log::trace!("PeerHandle: Received shutdown"); log::trace!("PeerHandle: Received shutdown");
if let Some(bandwidth_manager) = &self.bandwidth_storage_manager {
if let Err(e) = bandwidth_manager.write().await.sync_storage_bandwidth().await {
log::error!("Storage sync failed - {e}, unaccounted bandwidth might have been consumed");
}
}
log::trace!("PeerHandle: Finished shutdown");
} }
} }
} }
@@ -0,0 +1,138 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use defguard_wireguard_rs::host::Peer;
use nym_gateway_storage::models::WireguardPeer;
use nym_gateway_storage::GatewayStorage;
use std::time::Duration;
use time::OffsetDateTime;
const DEFAULT_PEER_MAX_FLUSHING_RATE: Duration = Duration::from_secs(60 * 60 * 24); // 24h
const DEFAULT_PEER_MAX_DELTA_FLUSHING_AMOUNT: u64 = 512 * 1024 * 1024; // 512MB
#[derive(Debug, Clone, Copy)]
pub struct PeerFlushingBehaviourConfig {
/// Defines maximum delay between peer information being flushed to the persistent storage.
pub peer_max_flushing_rate: Duration,
/// Defines a maximum change in peer before it gets flushed to the persistent storage.
pub peer_max_delta_flushing_amount: u64,
}
impl Default for PeerFlushingBehaviourConfig {
fn default() -> Self {
Self {
peer_max_flushing_rate: DEFAULT_PEER_MAX_FLUSHING_RATE,
peer_max_delta_flushing_amount: DEFAULT_PEER_MAX_DELTA_FLUSHING_AMOUNT,
}
}
}
pub struct PeerStorageManager {
pub(crate) storage: GatewayStorage,
pub(crate) peer_information: Option<PeerInformation>,
pub(crate) cfg: PeerFlushingBehaviourConfig,
pub(crate) with_client_id: bool,
}
impl PeerStorageManager {
pub(crate) fn new(storage: GatewayStorage, peer: Peer, with_client_id: bool) -> Self {
let peer_information = Some(PeerInformation::new(peer));
Self {
storage,
peer_information,
cfg: PeerFlushingBehaviourConfig::default(),
with_client_id,
}
}
pub(crate) fn get_peer(&self) -> Option<WireguardPeer> {
self.peer_information
.as_ref()
.map(|p| p.peer.clone().into())
}
pub(crate) fn remove_peer(&mut self) {
self.peer_information = None;
}
pub(crate) fn update_trx(&mut self, kernel_peer: &Peer) {
if let Some(peer_information) = self.peer_information.as_mut() {
peer_information.update_trx_bytes(kernel_peer.tx_bytes, kernel_peer.rx_bytes);
}
}
pub(crate) async fn sync_storage_peer(&mut self) -> Result<(), Error> {
let Some(peer_information) = self.peer_information.as_mut() else {
return Ok(());
};
if !peer_information.should_sync(self.cfg) {
return Ok(());
}
if self
.storage
.get_wireguard_peer(&peer_information.peer().public_key.to_string())
.await?
.is_none()
{
self.peer_information = None;
return Ok(());
}
self.storage
.insert_wireguard_peer(peer_information.peer(), self.with_client_id)
.await?;
peer_information.resync_peer_with_storage();
Ok(())
}
}
#[derive(Clone, Debug)]
pub(crate) struct PeerInformation {
pub(crate) peer: Peer,
pub(crate) last_synced: OffsetDateTime,
pub(crate) bytes_delta_since_sync: u64,
}
impl PeerInformation {
pub fn new(peer: Peer) -> PeerInformation {
PeerInformation {
peer,
last_synced: OffsetDateTime::now_utc(),
bytes_delta_since_sync: 0,
}
}
pub(crate) fn should_sync(&self, cfg: PeerFlushingBehaviourConfig) -> bool {
if self.bytes_delta_since_sync >= cfg.peer_max_delta_flushing_amount {
return true;
}
if self.last_synced + cfg.peer_max_flushing_rate < OffsetDateTime::now_utc()
&& self.bytes_delta_since_sync != 0
{
return true;
}
false
}
pub(crate) fn peer(&self) -> &Peer {
&self.peer
}
pub(crate) fn update_trx_bytes(&mut self, tx_bytes: u64, rx_bytes: u64) {
self.bytes_delta_since_sync += tx_bytes.saturating_sub(self.peer.tx_bytes)
+ rx_bytes.saturating_sub(self.peer.rx_bytes);
self.peer.tx_bytes = tx_bytes;
self.peer.rx_bytes = rx_bytes;
}
pub(crate) fn resync_peer_with_storage(&mut self) {
self.bytes_delta_since_sync = 0;
self.last_synced = OffsetDateTime::now_utc();
}
}
@@ -34,6 +34,28 @@ This page displays a full list of all the changes during our release cycle from
<VarInfo /> <VarInfo />
## `magura-drift`
Second patch to `v2024.13-magura` release version.
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-drift)
- [`nym-node`](nodes/nym-node.mdx) version `1.1.12`
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-29T13:10:51.813092288Z
Build Version: 1.1.12
Commit SHA: 4a9a5579c40ad956163ea02e01d7b53aef2ac8ef
Commit Date: 2024-11-29T14:06:32.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.83.0
rustc Channel: stable
cargo Profile: release
```
- This patch adds a peer storage manager to fix issues causing external clients to be blocked, ensuring they can successfully connect to different nodes.
## `v2024.13-magura-patched` ## `v2024.13-magura-patched`
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-patched) - [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-patched)
@@ -54,12 +76,11 @@ cargo Profile: release
<Callout type="warning" emoji="⚠️"> <Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Insstead of fixing this outdated explorer, we are working on a new one, coming out soon. After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](nodes/performance-and-testing/gateway-probe). [Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](nodes/performance-and-testing/gateway-probe).
</Callout> </Callout>
### Operators Updates & Tools ### Operators Updates & Tools
- Updated [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) (moved to our monorepo) helps operators to configure their IP tables rules for `nymtun` and `wireguard` routing. - Updated [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) (moved to our monorepo) helps operators to configure their IP tables rules for `nymtun` and `wireguard` routing.
@@ -53,7 +53,7 @@ journalctl -f -u nym-node.service
</Steps> </Steps>
<Callout type="warning" emoji="⚠️"> <Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Insstead of fixing this outdated explorer, we are working on a new one, coming out soon. After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](../performance-and-testing/gateway-probe). [Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](../performance-and-testing/gateway-probe).
</Callout> </Callout>
@@ -120,7 +120,7 @@ From `nym-wallet` version `1.2.15` onward the application allows and prompts ope
###### 2. Verify the binary and extract it if needed ###### 2. Verify the binary and extract it if needed
- Download [`hashes.json`]https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json - Download [`hashes.json`](https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json)
- Open it with your text editor or print it's content with `cat hashes.json` - Open it with your text editor or print it's content with `cat hashes.json`
- Run `sha256sum <WALLET_BINARY>` for example `sha256sum ./nym-wallet_1.2.15_amd64.AppImage` - Run `sha256sum <WALLET_BINARY>` for example `sha256sum ./nym-wallet_1.2.15_amd64.AppImage`
- If your have to extract it (like `.tar.gz`) do it - If your have to extract it (like `.tar.gz`) do it
@@ -17,12 +17,12 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
```sh ```sh
nym-node nym-node
Binary Name: nym-node Binary Name: nym-node
Build Timestamp: 2024-11-22T14:30:48.067329245Z Build Timestamp: 2024-11-29T13:10:51.813092288Z
Build Version: 1.1.11 Build Version: 1.1.12
Commit SHA: 01c7b2819ee3d328deccd303b4113ff415d7e276 Commit SHA: 4a9a5579c40ad956163ea02e01d7b53aef2ac8ef
Commit Date: 2024-11-22T10:50:59.000000000+01:00 Commit Date: 2024-11-29T14:06:32.000000000+01:00
Commit Branch: HEAD Commit Branch: HEAD
rustc Version: 1.82.0 rustc Version: 1.83.0
rustc Channel: stable rustc Channel: stable
cargo Profile: release cargo Profile: release
``` ```
+1
View File
@@ -20,5 +20,6 @@ REWARDING_VALIDATOR_ADDRESS=n1rfvpsynktze6wvn6ldskj8xgwfzzk5v6pnff39
EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api/ EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api/
NYXD=https://qa-validator.qa.nymte.ch NYXD=https://qa-validator.qa.nymte.ch
NYXD_WS=wss://qa-validator.qa.nymte.ch/websocket/
NYM_API=https://qa-nym-api.qa.nymte.ch/api/ NYM_API=https://qa-nym-api.qa.nymte.ch/api/
NYM_VPN_API=https://nym-vpn-api-git-deploy-qa-nyx-network-staging.vercel.app/api/ NYM_VPN_API=https://nym-vpn-api-git-deploy-qa-nyx-network-staging.vercel.app/api/
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "explorer-api" name = "explorer-api"
version = "1.1.42" version = "1.1.43"
edition = "2021" edition = "2021"
license.workspace = true license.workspace = true
+1 -1
View File
@@ -166,7 +166,7 @@ impl GeoIp {
} }
} }
impl<'a> TryFrom<&City<'a>> for Location { impl TryFrom<&City<'_>> for Location {
type Error = String; type Error = String;
fn try_from(city: &City) -> Result<Self, Self::Error> { fn try_from(city: &City) -> Result<Self, Self::Error> {
+1 -1
View File
@@ -65,7 +65,7 @@ impl<'r> FromRequest<'r> for Location {
} }
} }
impl<'a> OpenApiFromRequest<'a> for Location { impl OpenApiFromRequest<'_> for Location {
fn from_request_input( fn from_request_input(
_gen: &mut OpenApiGenerator, _gen: &mut OpenApiGenerator,
_name: String, _name: String,
+1 -2
View File
@@ -58,7 +58,6 @@ nym-mixnet-client = { path = "../common/client-libs/mixnet-client" }
nym-mixnode-common = { path = "../common/mixnode-common" } nym-mixnode-common = { path = "../common/mixnode-common" }
nym-network-defaults = { path = "../common/network-defaults" } nym-network-defaults = { path = "../common/network-defaults" }
nym-network-requester = { path = "../service-providers/network-requester" } nym-network-requester = { path = "../service-providers/network-requester" }
nym-node-http-api = { path = "../nym-node/nym-node-http-api" }
nym-sdk = { path = "../sdk/rust/nym-sdk" } nym-sdk = { path = "../sdk/rust/nym-sdk" }
nym-sphinx = { path = "../common/nymsphinx" } nym-sphinx = { path = "../common/nymsphinx" }
nym-statistics-common = { path = "../common/statistics" } nym-statistics-common = { path = "../common/statistics" }
@@ -67,13 +66,13 @@ nym-topology = { path = "../common/topology" }
nym-types = { path = "../common/types" } nym-types = { path = "../common/types" }
nym-validator-client = { path = "../common/client-libs/validator-client" } nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" } nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
nym-node-metrics = { path = "../nym-node/nym-node-metrics" }
nym-wireguard = { path = "../common/wireguard" } nym-wireguard = { path = "../common/wireguard" }
nym-wireguard-types = { path = "../common/wireguard-types", default-features = false } nym-wireguard-types = { path = "../common/wireguard-types", default-features = false }
defguard_wireguard_rs = { workspace = true } defguard_wireguard_rs = { workspace = true }
[build-dependencies] [build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [ sqlx = { workspace = true, features = [
+13 -147
View File
@@ -1,36 +1,19 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use nym_network_defaults::{DEFAULT_NYM_NODE_HTTP_PORT, TICKETBOOK_VALIDITY_DAYS}; use nym_network_defaults::TICKETBOOK_VALIDITY_DAYS;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::SocketAddr;
use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
use zeroize::{Zeroize, ZeroizeOnDrop};
// 'DEBUG' // TODO: can we move those away?
// where applicable, the below are defined in milliseconds pub const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5);
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000); pub const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5);
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB
#[derive(Debug)] #[derive(Debug)]
pub struct Config { pub struct Config {
pub host: Host,
pub http: Http,
pub gateway: Gateway, pub gateway: Gateway,
// pub storage_paths: GatewayPaths,
pub network_requester: NetworkRequester, pub network_requester: NetworkRequester,
pub ip_packet_router: IpPacketRouter, pub ip_packet_router: IpPacketRouter,
@@ -39,18 +22,13 @@ pub struct Config {
} }
impl Config { impl Config {
#[allow(clippy::too_many_arguments)] pub fn new(
pub fn externally_loaded(
host: impl Into<Host>,
http: impl Into<Http>,
gateway: impl Into<Gateway>, gateway: impl Into<Gateway>,
network_requester: impl Into<NetworkRequester>, network_requester: impl Into<NetworkRequester>,
ip_packet_router: impl Into<IpPacketRouter>, ip_packet_router: impl Into<IpPacketRouter>,
debug: impl Into<Debug>, debug: impl Into<Debug>,
) -> Self { ) -> Self {
Config { Config {
host: host.into(),
http: http.into(),
gateway: gateway.into(), gateway: gateway.into(),
network_requester: network_requester.into(), network_requester: network_requester.into(),
ip_packet_router: ip_packet_router.into(), ip_packet_router: ip_packet_router.into(),
@@ -65,96 +43,23 @@ impl Config {
pub fn get_nyxd_urls(&self) -> Vec<Url> { pub fn get_nyxd_urls(&self) -> Vec<Url> {
self.gateway.nyxd_urls.clone() self.gateway.nyxd_urls.clone()
} }
pub fn get_cosmos_mnemonic(&self) -> bip39::Mnemonic {
self.gateway.cosmos_mnemonic.clone()
}
} }
// TODO: this is very much a WIP. we need proper ssl certificate support here #[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
pub struct Host {
/// Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections.
pub public_ips: Vec<IpAddr>,
/// Optional hostname of this node, for example nymtech.net.
// TODO: this is temporary. to be replaced by pulling the data directly from the certs.
pub hostname: Option<String>,
}
impl Host {
pub fn validate(&self) -> bool {
if self.public_ips.is_empty() {
return false;
}
true
}
}
#[derive(Debug, PartialEq)]
pub struct Http {
/// Socket address this node will use for binding its http API.
/// default: `0.0.0.0:8000`
pub bind_address: SocketAddr,
/// Path to assets directory of custom landing page of this node.
pub landing_page_assets_path: Option<PathBuf>,
}
impl Default for Http {
fn default() -> Self {
Http {
bind_address: SocketAddr::new(
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
DEFAULT_NYM_NODE_HTTP_PORT,
),
landing_page_assets_path: None,
}
}
}
// we only really care about the mnemonic being zeroized
#[derive(Debug, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct Gateway { pub struct Gateway {
/// Version of the gateway for which this configuration was created. /// Indicates whether this gateway is accepting only zk-nym credentials for accessing the mixnet
pub version: String, /// or if it also accepts non-paying clients
pub enforce_zk_nyms: bool,
/// ID specifies the human readable ID of this particular gateway. /// Socket address this node will use for binding its client websocket API.
pub id: String, /// default: `0.0.0.0:9000`
pub websocket_bind_address: SocketAddr,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
/// the mixnet, or if it also accepts non-paying clients
pub only_coconut_credentials: bool,
/// Address to which this mixnode will bind to and will be listening for packets.
#[zeroize(skip)]
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
pub mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
pub clients_port: u16,
/// If applicable, announced port for listening for secure websocket client traffic.
/// (default: None)
pub clients_wss_port: Option<u16>,
/// Addresses to APIs from which the node gets the view of the network. /// Addresses to APIs from which the node gets the view of the network.
#[zeroize(skip)]
pub nym_api_urls: Vec<Url>, pub nym_api_urls: Vec<Url>,
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens. /// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
#[zeroize(skip)]
pub nyxd_urls: Vec<Url>, pub nyxd_urls: Vec<Url>,
/// Mnemonic of a cosmos wallet used in checking for double spending.
// #[deprecated(note = "move to storage")]
// TODO: I don't think this should be stored directly in the config...
pub cosmos_mnemonic: bip39::Mnemonic,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@@ -185,60 +90,21 @@ impl Default for IpPacketRouter {
#[derive(Debug)] #[derive(Debug)]
pub struct Debug { pub struct Debug {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Delay between each subsequent presence data being sent.
// DEAD FIELD
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
// DEAD FIELD
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Defines maximum delay between client bandwidth information being flushed to the persistent storage. /// Defines maximum delay between client bandwidth information being flushed to the persistent storage.
pub client_bandwidth_max_flushing_rate: Duration, pub client_bandwidth_max_flushing_rate: Duration,
/// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage. /// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage.
pub client_bandwidth_max_delta_flushing_amount: i64, pub client_bandwidth_max_delta_flushing_amount: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
pub use_legacy_framed_packet_version: bool,
pub zk_nym_tickets: ZkNymTicketHandlerDebug, pub zk_nym_tickets: ZkNymTicketHandlerDebug,
} }
impl Default for Debug { impl Default for Debug {
fn default() -> Self { fn default() -> Self {
Debug { Debug {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
client_bandwidth_max_flushing_rate: DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE, client_bandwidth_max_flushing_rate: DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE,
client_bandwidth_max_delta_flushing_amount: client_bandwidth_max_delta_flushing_amount:
DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT, DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT,
use_legacy_framed_packet_version: false,
zk_nym_tickets: Default::default(), zk_nym_tickets: Default::default(),
} }
} }
+4 -10
View File
@@ -3,7 +3,7 @@
use nym_authenticator::error::AuthenticatorError; use nym_authenticator::error::AuthenticatorError;
use nym_gateway_stats_storage::error::StatsStorageError; use nym_gateway_stats_storage::error::StatsStorageError;
use nym_gateway_storage::error::StorageError; use nym_gateway_storage::error::GatewayStorageError;
use nym_ip_packet_router::error::IpPacketRouterError; use nym_ip_packet_router::error::IpPacketRouterError;
use nym_network_requester::error::{ClientCoreError, NetworkRequesterError}; use nym_network_requester::error::{ClientCoreError, NetworkRequesterError};
use nym_validator_client::nyxd::error::NyxdError; use nym_validator_client::nyxd::error::NyxdError;
@@ -38,7 +38,7 @@ pub enum GatewayError {
#[error("storage failure: {source}")] #[error("storage failure: {source}")]
StorageError { StorageError {
#[from] #[from]
source: StorageError, source: GatewayStorageError,
}, },
#[error("stats storage failure: {source}")] #[error("stats storage failure: {source}")]
@@ -74,14 +74,8 @@ pub enum GatewayError {
source: AuthenticatorError, source: AuthenticatorError,
}, },
#[error("failed to startup local network requester")] #[error("failed to startup local {typ}")]
NetworkRequesterStartupFailure, ServiceProviderStartupFailure { typ: &'static str },
#[error("failed to startup local ip packet router")]
IpPacketRouterStartupFailure,
#[error("failed to startup local authenticator")]
AuthenticatorStartupFailure,
#[error("there are no nym API endpoints available")] #[error("there are no nym API endpoints available")]
NoNymApisAvailable, NoNymApisAvailable,
+1 -1
View File
@@ -9,4 +9,4 @@ pub mod error;
pub mod node; pub mod node;
pub use error::GatewayError; pub use error::GatewayError;
pub use node::Gateway; pub use node::GatewayTasksBuilder;
@@ -5,8 +5,6 @@ use super::websocket::message_receiver::{IsActiveRequestSender, MixMessageSender
use crate::node::client_handling::embedded_clients::LocalEmbeddedClientHandle; use crate::node::client_handling::embedded_clients::LocalEmbeddedClientHandle;
use dashmap::DashMap; use dashmap::DashMap;
use nym_sphinx::DestinationAddressBytes; use nym_sphinx::DestinationAddressBytes;
use nym_statistics_common::gateways;
use nym_statistics_common::gateways::GatewayStatsReporter;
use std::sync::Arc; use std::sync::Arc;
use tracing::warn; use tracing::warn;
@@ -34,10 +32,9 @@ impl ActiveClient {
} }
} }
#[derive(Clone)] #[derive(Clone, Default)]
pub(crate) struct ActiveClientsStore { pub struct ActiveClientsStore {
inner: Arc<DashMap<DestinationAddressBytes, ActiveClient>>, inner: Arc<DashMap<DestinationAddressBytes, ActiveClient>>,
stats_event_reporter: GatewayStatsReporter,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -51,11 +48,8 @@ pub(crate) struct ClientIncomingChannels {
impl ActiveClientsStore { impl ActiveClientsStore {
/// Creates new instance of `ActiveClientsStore` to store in-memory handles to all currently connected clients. /// Creates new instance of `ActiveClientsStore` to store in-memory handles to all currently connected clients.
pub(crate) fn new(stats_event_reporter: GatewayStatsReporter) -> Self { pub fn new() -> Self {
ActiveClientsStore { Self::default()
inner: Arc::new(DashMap::new()),
stats_event_reporter,
}
} }
/// Tries to obtain sending channel to specified client. Note that if stale entry existed, it is /// Tries to obtain sending channel to specified client. Note that if stale entry existed, it is
@@ -64,7 +58,7 @@ impl ActiveClientsStore {
/// # Arguments /// # Arguments
/// ///
/// * `client`: address of the client for which to obtain the handle. /// * `client`: address of the client for which to obtain the handle.
pub(crate) fn get_sender(&self, client: DestinationAddressBytes) -> Option<MixMessageSender> { pub fn get_sender(&self, client: DestinationAddressBytes) -> Option<MixMessageSender> {
let entry = self.inner.get(&client)?; let entry = self.inner.get(&client)?;
let handle = entry.value().get_sender(); let handle = entry.value().get_sender();
@@ -130,8 +124,6 @@ impl ActiveClientsStore {
/// * `client`: address of the client for which to remove the handle. /// * `client`: address of the client for which to remove the handle.
pub(crate) fn disconnect(&self, client: DestinationAddressBytes) { pub(crate) fn disconnect(&self, client: DestinationAddressBytes) {
self.inner.remove(&client); self.inner.remove(&client);
self.stats_event_reporter
.report(gateways::GatewayStatsEvent::new_session_stop(client));
} }
/// Insert new client handle into the store. /// Insert new client handle into the store.
@@ -153,12 +145,10 @@ impl ActiveClientsStore {
if self.inner.insert(client, entry).is_some() { if self.inner.insert(client, entry).is_some() {
panic!("inserted a duplicate remote client") panic!("inserted a duplicate remote client")
} }
self.stats_event_reporter
.report(gateways::GatewayStatsEvent::new_session_start(client));
} }
/// Inserts a handle to the embedded client /// Inserts a handle to the embedded client
pub(crate) fn insert_embedded(&self, local_client_handle: LocalEmbeddedClientHandle) { pub fn insert_embedded(&self, local_client_handle: LocalEmbeddedClientHandle) {
let key = local_client_handle.client_destination(); let key = local_client_handle.client_destination();
let entry = ActiveClient::Embedded(local_client_handle); let entry = ActiveClient::Embedded(local_client_handle);
if self.inner.insert(key, entry).is_some() { if self.inner.insert(key, entry).is_some() {
@@ -9,10 +9,11 @@ use nym_network_requester::{GatewayPacketRouter, PacketRouter};
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::DestinationAddressBytes; use nym_sphinx::DestinationAddressBytes;
use nym_task::TaskClient; use nym_task::TaskClient;
use tokio::task::JoinHandle;
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct LocalEmbeddedClientHandle { pub struct LocalEmbeddedClientHandle {
/// Nym address of the embedded client. /// Nym address of the embedded client.
pub(crate) address: Recipient, pub(crate) address: Recipient,
@@ -52,8 +53,8 @@ impl MessageRouter {
} }
} }
pub(crate) fn start_with_shutdown(self, shutdown: TaskClient) { pub(crate) fn start_with_shutdown(self, shutdown: TaskClient) -> JoinHandle<()> {
tokio::spawn(self.run_with_shutdown(shutdown)); tokio::spawn(self.run_with_shutdown(shutdown))
} }
fn handle_received_messages(&self, messages: Vec<Vec<u8>>) { fn handle_received_messages(&self, messages: Vec<Vec<u8>>) {
@@ -1,18 +1,23 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::node::ActiveClientsStore;
use nym_credential_verification::{ecash::EcashManager, BandwidthFlushingBehaviourConfig}; use nym_credential_verification::{ecash::EcashManager, BandwidthFlushingBehaviourConfig};
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_statistics_common::gateways::GatewayStatsReporter; use nym_gateway_storage::GatewayStorage;
use nym_mixnet_client::forwarder::MixForwardingSender;
use nym_node_metrics::events::MetricEventsSender;
use std::sync::Arc; use std::sync::Arc;
// I can see this being possible expanded with say storage or client store // I can see this being possible expanded with say storage or client store
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct CommonHandlerState<S> { pub(crate) struct CommonHandlerState {
pub(crate) ecash_verifier: Arc<EcashManager<S>>, pub(crate) ecash_verifier: Arc<EcashManager>,
pub(crate) storage: S, pub(crate) storage: GatewayStorage,
pub(crate) local_identity: Arc<identity::KeyPair>, pub(crate) local_identity: Arc<identity::KeyPair>,
pub(crate) only_coconut_credentials: bool, pub(crate) only_coconut_credentials: bool,
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig, pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
pub(crate) stats_event_reporter: GatewayStatsReporter, pub(crate) metrics_sender: MetricEventsSender,
pub(crate) outbound_mix_sender: MixForwardingSender,
pub(crate) active_clients_store: ActiveClientsStore,
} }
@@ -24,9 +24,10 @@ use nym_gateway_requests::{
ClientControlRequest, ClientRequest, GatewayRequestsError, SensitiveServerResponse, ClientControlRequest, ClientRequest, GatewayRequestsError, SensitiveServerResponse,
SimpleGatewayRequestsError, SimpleGatewayRequestsError,
}; };
use nym_gateway_storage::{error::StorageError, Storage}; use nym_gateway_storage::error::GatewayStorageError;
use nym_node_metrics::events::MetricsEvent;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_statistics_common::gateways; use nym_statistics_common::gateways::GatewaySessionEvent;
use nym_task::TaskClient; use nym_task::TaskClient;
use nym_validator_client::coconut::EcashApiError; use nym_validator_client::coconut::EcashApiError;
use rand::{random, CryptoRng, Rng}; use rand::{random, CryptoRng, Rng};
@@ -39,7 +40,7 @@ use tracing::*;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum RequestHandlingError { pub enum RequestHandlingError {
#[error("Internal gateway storage error")] #[error("Internal gateway storage error")]
StorageError(#[from] StorageError), StorageError(#[from] GatewayStorageError),
#[error( #[error(
"the database entry for bandwidth of the registered client {client_address} is missing!" "the database entry for bandwidth of the registered client {client_address} is missing!"
@@ -136,9 +137,9 @@ impl IntoWSMessage for ServerResponse {
} }
} }
pub(crate) struct AuthenticatedHandler<R, S, St> { pub(crate) struct AuthenticatedHandler<R, S> {
inner: FreshHandler<R, S, St>, inner: FreshHandler<R, S>,
bandwidth_storage_manager: BandwidthStorageManager<St>, bandwidth_storage_manager: BandwidthStorageManager,
client: ClientDetails, client: ClientDetails,
mix_receiver: MixMessageReceiver, mix_receiver: MixMessageReceiver,
// Occasionally the handler is requested to ping the connected client for confirm that it's // Occasionally the handler is requested to ping the connected client for confirm that it's
@@ -149,20 +150,13 @@ pub(crate) struct AuthenticatedHandler<R, S, St> {
} }
// explicitly remove handle from the global store upon being dropped // explicitly remove handle from the global store upon being dropped
impl<R, S, St> Drop for AuthenticatedHandler<R, S, St> { impl<R, S> Drop for AuthenticatedHandler<R, S> {
fn drop(&mut self) { fn drop(&mut self) {
self.inner self.disconnect_client()
.active_clients_store
.disconnect(self.client.address)
} }
} }
impl<R, S, St> AuthenticatedHandler<R, S, St> impl<R, S> AuthenticatedHandler<R, S> {
where
// TODO: those trait bounds here don't really make sense....
R: Rng + CryptoRng,
St: Storage + Clone + 'static,
{
/// Upgrades `FreshHandler` into the Authenticated variant implying the client is now authenticated /// Upgrades `FreshHandler` into the Authenticated variant implying the client is now authenticated
/// and thus allowed to perform more actions with the gateway, such as redeeming bandwidth or /// and thus allowed to perform more actions with the gateway, such as redeeming bandwidth or
/// sending sphinx packets. /// sending sphinx packets.
@@ -173,7 +167,7 @@ where
/// * `client`: details (i.e. address and shared keys) of the registered client /// * `client`: details (i.e. address and shared keys) of the registered client
/// * `mix_receiver`: channel used for receiving messages from the mixnet destined for this client. /// * `mix_receiver`: channel used for receiving messages from the mixnet destined for this client.
pub(crate) async fn upgrade( pub(crate) async fn upgrade(
fresh: FreshHandler<R, S, St>, fresh: FreshHandler<R, S>,
client: ClientDetails, client: ClientDetails,
mix_receiver: MixMessageReceiver, mix_receiver: MixMessageReceiver,
is_active_request_receiver: IsActiveRequestReceiver, is_active_request_receiver: IsActiveRequestReceiver,
@@ -191,7 +185,7 @@ where
client_address: client.address.as_base58_string(), client_address: client.address.as_base58_string(),
})?; })?;
Ok(AuthenticatedHandler { let handler = AuthenticatedHandler {
bandwidth_storage_manager: BandwidthStorageManager::new( bandwidth_storage_manager: BandwidthStorageManager::new(
fresh.shared_state.storage.clone(), fresh.shared_state.storage.clone(),
ClientBandwidth::new(bandwidth.into()), ClientBandwidth::new(bandwidth.into()),
@@ -204,14 +198,24 @@ where
mix_receiver, mix_receiver,
is_active_request_receiver, is_active_request_receiver,
is_active_ping_pending_reply: None, is_active_ping_pending_reply: None,
}) };
handler.send_metrics(GatewaySessionEvent::new_session_start(
handler.client.address,
));
Ok(handler)
} }
/// Explicitly removes handle from the global store. fn disconnect_client(&mut self) {
fn disconnect(self) {
self.inner self.inner
.shared_state
.active_clients_store .active_clients_store
.disconnect(self.client.address) .disconnect(self.client.address);
self.send_metrics(GatewaySessionEvent::new_session_stop(self.client.address));
}
fn send_metrics(&self, event: impl Into<MetricsEvent>) {
self.inner.send_metrics(event)
} }
/// Forwards the received mix packet from the client into the mix network. /// Forwards the received mix packet from the client into the mix network.
@@ -220,7 +224,12 @@ where
/// ///
/// * `mix_packet`: packet received from the client that should get forwarded into the network. /// * `mix_packet`: packet received from the client that should get forwarded into the network.
fn forward_packet(&self, mix_packet: MixPacket) { fn forward_packet(&self, mix_packet: MixPacket) {
if let Err(err) = self.inner.outbound_mix_sender.unbounded_send(mix_packet) { if let Err(err) = self
.inner
.shared_state
.outbound_mix_sender
.forward_packet(mix_packet)
{
error!("We failed to forward requested mix packet - {err}. Presumably our mix forwarder has crashed. We cannot continue."); error!("We failed to forward requested mix packet - {err}. Presumably our mix forwarder has crashed. We cannot continue.");
process::exit(1); process::exit(1);
} }
@@ -259,8 +268,8 @@ where
trace!("available total bandwidth: {available_total}"); trace!("available total bandwidth: {available_total}");
if let Ok(ticket_type) = maybe_ticket_type { if let Ok(ticket_type) = maybe_ticket_type {
self.inner.shared_state.stats_event_reporter.report( self.inner.shared_state.metrics_sender.report_unchecked(
gateways::GatewayStatsEvent::new_ecash_ticket(self.client.address, ticket_type), GatewaySessionEvent::new_ecash_ticket(self.client.address, ticket_type),
); );
} else { } else {
error!("Somehow verified a ticket with an unknown ticket type"); error!("Somehow verified a ticket with an unknown ticket type");
@@ -372,7 +381,10 @@ where
/// # Arguments /// # Arguments
/// ///
/// * `raw_request`: raw message to handle. /// * `raw_request`: raw message to handle.
async fn handle_text(&mut self, raw_request: String) -> Message { async fn handle_text(&mut self, raw_request: String) -> Message
where
R: Rng + CryptoRng,
{
trace!("text request"); trace!("text request");
let request = match ClientControlRequest::try_from(raw_request) { let request = match ClientControlRequest::try_from(raw_request) {
@@ -470,7 +482,10 @@ where
client = %self.client.address.as_base58_string() client = %self.client.address.as_base58_string()
) )
)] )]
async fn handle_request(&mut self, raw_request: Message) -> Option<Message> { async fn handle_request(&mut self, raw_request: Message) -> Option<Message>
where
R: Rng + CryptoRng,
{
trace!("new request"); trace!("new request");
// apparently tungstenite auto-handles ping/pong/close messages so for now let's ignore // apparently tungstenite auto-handles ping/pong/close messages so for now let's ignore
@@ -542,8 +557,8 @@ where
/// and for sphinx packets received from the mix network that should be sent back to the client. /// and for sphinx packets received from the mix network that should be sent back to the client.
pub(crate) async fn listen_for_requests(mut self, mut shutdown: TaskClient) pub(crate) async fn listen_for_requests(mut self, mut shutdown: TaskClient)
where where
R: Rng + CryptoRng,
S: AsyncRead + AsyncWrite + Unpin, S: AsyncRead + AsyncWrite + Unpin,
St: Storage,
{ {
trace!("Started listening for ALL incoming requests..."); trace!("Started listening for ALL incoming requests...");
@@ -612,7 +627,6 @@ where
} }
} }
self.disconnect();
trace!("The stream was closed!"); trace!("The stream was closed!");
} }
} }

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