Compare commits

...

79 Commits

Author SHA1 Message Date
mfahampshire 7f1baa1db7 sandbox nyx rest api 2024-12-18 14:34:06 +01:00
mfahampshire 6d4eaea1cc spacing + working openapi local for nymapi 2024-12-18 14:29:28 +01:00
dynco-nym 865b668e12 Move ecash schema out of ecash crate 2024-12-18 13:23:17 +01:00
dynco-nym c4409e3c1a Improvements 2024-12-16 19:08:53 +01:00
dynco-nym 3934c556ee generate Sqlx schema files 2024-12-16 13:47:04 +01:00
dynco-nym f0da36df7c Gitattributes to ignore .sqlx diffs 2024-12-16 13:46:43 +01:00
dynco-nym 95989dbb67 Post rebase fixes 2024-12-16 13:32:06 +01:00
dynco-nym 33f2e2ca7d WIP 2024-12-16 13:24:34 +01:00
dynco-nym fce494af97 Compiles with utoipa 5.2 2024-12-16 13:24:33 +01:00
dynco-nym 671ce9a399 A bunch of annotations 2024-12-16 12:05:54 +01:00
dynco-nym 1303d404f7 Add cfg_attr 2024-12-16 12:05:54 +01:00
dynco-nym 7618ebf694 rustfmt 2024-12-16 12:05:54 +01:00
dynco-nym 13e64da2ad ContractBuildInformation on /nym_contracts_detailed 2024-12-16 12:05:54 +01:00
dynco-nym 901b88f98b Derive ToSchema for more types 2024-12-16 12:05:54 +01:00
dynco-nym b60f07730b WIP adding derive(ToSchema) 2024-12-16 12:05:54 +01:00
Jon Häggblad c0b4e8dd70 Remove unneeded async function annotation (#5246) 2024-12-16 09:15:46 +01:00
Fran Arbanas e7702a1e7a fix: remove documentation from dockerignore since it's refernced in Cargo.toml (#5264) 2024-12-13 14:44:36 +01:00
windy-ux 07435ce3b2 Fix/web 615 seo setup (#5257)
* + add header into Packet Mixing docs

* + add head changes for testing

* / updated version of metatags in theme.config

* + add env file

* / theme.config to use NEXT_PUBLIC_SITE_URL from env file

* @ Fix broken link in theme.config

* - remove favicon code

* + add desription for intro pages
2024-12-13 13:09:49 +00:00
benedetta davico 9690c73c91 Merge pull request #5261 from nymtech/merge/release/2024.14-crunch
update changelog for crunch
2024-12-13 11:41:00 +01:00
Jędrzej Stuczyński 684d7ac1a2 removed legacy socks5 listener (#5259) 2024-12-13 10:03:43 +00:00
Jędrzej Stuczyński b813044360 bugfix: make sure to apply gateway score filtering when choosing initial node (#5256)
* bugfix: make sure to apply gateway score filtering when choosing initial node

* mixfetch build fix
2024-12-13 09:09:56 +00:00
Bogdan-Ștefan Neacşu c26d4f24fc Add conversion unit tests for auth msg (#5251)
* Add conversion unit tests for auth msg

* Fix remaining bad mac conversions
2024-12-13 10:38:25 +02:00
Drazen Urch ee7b3f1415 Update TS bindings (#5255) 2024-12-12 14:21:57 +00:00
import this ccd66f8a51 tokenomics edits (#5254) 2024-12-12 13:34:03 +00:00
mfahampshire c31d1f63e6 updated readme links + bit of general clean (#5253) 2024-12-12 13:11:25 +00:00
import this 2ab172146a [DOCs/operators]: Edit tokenomics definitions (#5252 ) 2024-12-12 11:24:38 +00:00
mfahampshire 9b5e14c78e tweak fix (#5250)
* tweak fix

* added default config directories
2024-12-12 09:52:04 +00:00
import this d9e5c62b5c Update changelog.mdx (#5248) 2024-12-11 17:00:36 +00:00
mfahampshire a336893116 Max/openapi docs (#5219)
* first pass redoc apis

* new landing + component update

* added intro

* new structure

* link list

* add sandbox sdk

* remove theme colours

* revert credit to ticket & ticketbook and actually get all the instances to replace

* Max/zknym doc tweak (#5223)

* revert credit to ticket & ticketbook

* revert credit to ticket & ticketbook and actually get all the instances to replace

* theme tweak to widen text area

* theme redoc component

* tweak padding topbar

* modified socks5 page to be in line with websocket client

* modify h size of autodoc generated command info

* tweak script to build from master

* add autodoc to workspace

* auto commit generated command files

* clean autodoc-generated-markdown in script

* auto commit generated command files

* tweak works

* clippy

* fix borked toml from cherrypick

* remove rm command

* auto commit generated command files

* blow away images

* auto commit generated command files

* remove redoc for nymapi for the moment but retain everything else

* fix double paste

* temp remove sandbox
2024-12-11 16:30:17 +00:00
mfahampshire 1d0d62f798 nymvpncli guide (#5243)
* nymvpn guide

* move nymvpn guide frm operators -> developers
2024-12-11 16:00:26 +00:00
import this daa680d6b8 [DOCs/operators]: Release notes v2024.14-crunch & config score updates (#5222)
* initialise tokenomics graph

* generate reward version config graph

* update tokenomics

* edit typo

* initialise release crunch release notes

* operators update

* add points to changelog

* update version graph and selection

* update iptables configuration

* add features to changelog

* comment redundant

* address review comments

* PR finish
2024-12-11 15:49:22 +00:00
Jędrzej Stuczyński fd47768b75 Merge pull request #5242 from nymtech/merge/release/2024.14-crunch
Merge/release/2024.14-crunch
2024-12-10 15:41:11 +00:00
Jędrzej Stuczyński 4e2aa2c0b3 Merge branch 'release/2024.14-crunch' into merge/release/2024.14-crunch 2024-12-10 15:29:26 +00: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
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
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
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
dynco-nym e7f806219c Move NS client to separate package under NS API (#5171)
* Move client code to NS API

* Move client to separate package

* Move things around

* Adjust run scripts

* rustfmt

* Add client to workspace
2024-11-26 15:59:42 +01:00
import this edd3f9108a [DOCs/operators]: Guide to change wg private address (#5178) 2024-11-26 09:32:09 +00:00
Tommy Verrall 3c56977fb5 Merge pull request #5176 from nymtech/script-update
Script update
2024-11-25 17:35:41 +00:00
Tommy Verrall 5f3bb5db82 remove command features 2024-11-25 17:52:49 +01:00
Tommy Verrall 1b84639c34 re-add the configure icmp command 2024-11-25 17:48:03 +01:00
Tommy Verrall 546a486f9f script overhaul
- improved iptables management: apply_iptables_rules and apply_iptables_rules_wg now automatically remove duplicate rules before reapplying them, ensuring a clean setup without disrupting iptables
- consolidated joke feature: unified the "joke through the mixnet" logic into a generic function, allowing it to work seamlessly across any specified interface
- enhanced tunnel checks: added check_nym_wg_tun alongside check_nymtun_iptables, making it easier to verify the state of both tunnels
- reduced error-prone behavior: simplified workflows to avoid issues caused by running commands multiple times

how to use:
1. download the script and make it executable:
   curl -L -o network_tunnel_manager.sh https://raw.download.github.of.this.file && chmod u+x network_tunnel_manager.sh

2. run the following commands as needed:
   - apply_iptables_rules: apply and clean iptables rules for nymtun0
   - apply_iptables_rules_wg: apply and clean iptables rules for nymwg
   - check_ipv6_ipv4_forwarding: verify if ipv4 and ipv6 forwarding are enabled
   - check_ip_routing: display the current ipv4 and ipv6 routing tables

tldr:
- improved iptables handling to avoid duplicates
- unified functionality for better maintainability
- reduced potential errors when rerunning commands
2024-11-25 17:45:10 +01:00
Jędrzej Stuczyński 5668e123d9 introduced initial internal commands for nym-cli: ecash key and request generation (#5174)
* introduced initial internal commands for nym-cli: ecash key and request generation

* reduced args logging level
2024-11-25 15:41:49 +00:00
import this 27637ae6b4 [DOCs/operators]: Routine guides update with release changes (#5173)
* finish doc updates - ready for review

* info to warning change

* add non root guide and a new error

* syntax fix

* syntax edit
2024-11-25 14:27:52 +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
646 changed files with 23291 additions and 12578 deletions
-1
View File
@@ -4,4 +4,3 @@
**/node_modules
**/target
dist
documentation
+1
View File
@@ -0,0 +1 @@
nym-validator-rewarder/.sqlx/** diff=nodiff
+1
View File
@@ -34,6 +34,7 @@ jobs:
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
@@ -56,14 +56,6 @@ jobs:
rustup target add aarch64-linux-android \
x86_64-linux-android
- name: Build lib nym-socks5-listener
working-directory: sdk/lib/socks5-listener/
env:
RELEASE: true
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
# build for arm64 and x86_64
run: ./build-android.sh aarch64 x86_64
- name: Build APKs (unsigned)
working-directory: nym-connect/native/android
env:
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
uses: mikefarah/yq@v4.44.6
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
uses: mikefarah/yq@v4.44.6
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
uses: mikefarah/yq@v4.44.6
id: get_version
with:
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
env:
WORKING_DIRECTORY: "nym-node-status-agent"
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent"
CONTAINER_NAME: "node-status-agent"
jobs:
@@ -31,7 +31,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
uses: mikefarah/yq@v4.44.6
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -58,4 +58,4 @@ jobs:
- name: BuildAndPushImageOnHarbor
run: |
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
docker 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:
env:
WORKING_DIRECTORY: "nym-node-status-api"
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
CONTAINER_NAME: "node-status-api"
jobs:
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
uses: mikefarah/yq@v4.44.6
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
uses: mikefarah/yq@v4.44.6
id: get_version
with:
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"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
uses: mikefarah/yq@v4.44.6
id: get_version
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
run: |
+49
View File
@@ -0,0 +1,49 @@
# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.
# See https://redocly.com/docs/cli/ for more information.
formatted-openapi.json:
path-parameters-defined:
# - >-
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/0/name
# - >-
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/1/name
# - >-
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/2/name
# - >-
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/3/name
# - >-
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/4/name
# - >-
# #/paths/~1v1~1status~1mixnode~1{mix_id}~1compute-reward-estimation/post/parameters/5/name
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/0/name'
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/1/name'
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/2/name'
# - '#/paths/~1v1~1unstable~1nym-nodes~1skimmed~1active/get/parameters/3/name'
operation-operationId-unique:
- >-
#/paths/~1v1~1status~1mixnodes~1active~1detailed/get/get_active_set_detailed
- '#/paths/~1v1~1status~1mixnodes~1detailed/get/get_mixnodes_detailed'
- >-
#/paths/~1v1~1status~1mixnodes~1rewarded~1detailed/get/get_rewarded_set_detailed
no-unused-components:
- '#/components/schemas/AxumErrorResponse'
- '#/components/schemas/DateQuery'
- '#/components/schemas/EcashTicketVerificationRejection'
- '#/components/schemas/ExpirationDatePathParam'
- '#/components/schemas/FullFatNode'
- '#/components/schemas/HistoricalPerformanceResponse'
- '#/components/schemas/HistoricalUptimeResponse'
- '#/components/schemas/MasterVerificationKeyResponse'
- '#/components/schemas/MixnodeStatusReport'
- '#/components/schemas/NodeId'
- '#/components/schemas/NodeRoleQueryParam'
- '#/components/schemas/NoiseDetails'
- '#/components/schemas/NymNodeDescription'
- '#/components/schemas/NymNodeDetails'
- '#/components/schemas/PaginationRequest'
- '#/components/schemas/PartialCoinIndicesSignatureResponse'
- '#/components/schemas/SpentCredentialsResponse'
- '#/components/schemas/UptimeHistoryResponse'
- '#/components/schemas/VerifyEcashCredentialBody'
- '#/components/responses/AxumErrorResponse'
- '#/components/responses/CirculatingSupplyResponse'
- '#/components/responses/RequestError'
+83
View File
@@ -0,0 +1,83 @@
extends:
- minimal
apis:
nym-api:
root: ./formatted-openapi.json
rules:
# https://redocly.com/docs/cli/rules/oas/operation-summary
operation-summary: off
# https://redocly.com/docs/cli/rules/oas/security-defined
security-defined: off
struct: off
# https://redocly.com/docs/cli/rules/oas/operation-2xx-response
operation-2xx-response: off
# rules:
# skip-warnings: true
# ignore:
# - path: /v1/gateways
# method: get
# - path: /v1/gateways/blacklisted
# method: get
# - path: /v1/mixnodes
# method: get
# - path: /v1/mixnodes/active
# method: get
# - path: /v1/mixnodes/active/detailed
# method: get
# - path: /v1/mixnodes/blacklisted
# method: get
# - path: /v1/mixnodes/detailed
# method: get
# - path: /v1/mixnodes/rewarded
# method: get
# - path: /v1/mixnodes/rewarded/detailed
# method: get
# - path: /v1/gateways/described
# method: get
# # network-monitor-status (deprecated)
# - path: /v1/status/gateway/{identity}/avg_uptime
# method: GET
# - path: /v1/status/gateway/{identity}/core-status-count
# method: GET
# - path: /v1/status/gateway/{identity}/history
# method: GET
# - path: /v1/status/gateway/{identity}/report
# method: GET
# - path: /v1/status/gateways/detailed
# method: GET
# - path: /v1/status/gateways/detailed-unfiltered
# method: GET
# - path: /v1/status/mixnode/{mix_id}/avg_uptime
# method: GET
# - path: /v1/status/mixnode/{mix_id}/compute-reward-estimation
# method: POST
# - path: /v1/status/mixnode/{mix_id}/core-status-count
# method: GET
# - path: /v1/status/mixnode/{mix_id}/history
# method: GET
# - path: /v1/status/mixnode/{mix_id}/report
# method: GET
# - path: /v1/status/mixnode/{mix_id}/reward-estimation
# method: GET
# - path: /v1/status/mixnodes/detailed-unfiltered
# method: GET
# # status
# - path: /v1/status/mixnode/{mix_id}/inclusion-probability
# method: GET
# - path: /v1/status/mixnode/{mix_id}/stake-saturation
# method: GET
# - path: /v1/status/mixnode/{mix_id}/status
# method: GET
# - path: /v1/status/mixnodes/active/detailed
# method: GET
# - path: /v1/status/mixnodes/detailed
# method: GET
# - path: /v1/status/mixnodes/inclusion-probability
# method: GET
# - path: /v1/status/mixnodes/rewarded/detailed
# method: GET
# # unstable nym nodes
# - path: /v1/unstable/nym-nodes/gateways/skimmed
# method: get
# - path: /v1/unstable/nym-nodes/mixnodes/skimmed
# method: get
Generated
+232 -480
View File
File diff suppressed because it is too large Load Diff
+12 -20
View File
@@ -61,7 +61,6 @@ members = [
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/models",
"common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
@@ -99,17 +98,15 @@ members = [
"common/wasm/utils",
"common/wireguard",
"common/wireguard-types",
# "documentation/autodoc",
"documentation/autodoc",
"explorer-api",
"explorer-api/explorer-api-requests",
"explorer-api/explorer-client",
"gateway",
"integrations/bity",
"mixnode",
"sdk/ffi/cpp",
"sdk/ffi/go",
"sdk/ffi/shared",
"sdk/lib/socks5-listener",
"sdk/rust/nym-sdk",
"service-providers/authenticator",
"service-providers/common",
@@ -124,10 +121,11 @@ members = [
"nym-data-observatory",
"nym-network-monitor",
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-node-status-api",
"nym-node-status-agent",
"nym-node/nym-node-metrics",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
@@ -149,23 +147,20 @@ members = [
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/internal/testnet-manager/dkg-bypass-contract", "common/verloc", "tools/internal/mixnet-connectivity-check",
]
default-members = [
"clients/native",
"clients/socks5",
"common/models",
"explorer-api",
"gateway",
"mixnode",
"nym-api",
"nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory",
"nym-node",
"nym-node-status-api",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-validator-rewarder",
"nym-node-status-api",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
@@ -264,6 +259,7 @@ http-body-util = "0.1"
httpcodec = "0.2.3"
humantime = "2.1.0"
humantime-serde = "1.1.1"
human-repr = "1.1.0"
hyper = "1.4.1"
hyper-util = "0.1"
indicatif = "0.17.8"
@@ -349,9 +345,9 @@ tracing-log = "0.2"
ts-rs = "10.0.0"
tungstenite = { version = "0.20.1", default-features = false }
url = "2.5"
utoipa = "4.2"
utoipa-swagger-ui = "7.1"
utoipauto = "0.1"
utoipa = "5.2"
utoipa-swagger-ui = "8.0"
utoipauto = "0.2"
uuid = "*"
vergen = { version = "=8.3.1", default-features = false }
walkdir = "2"
@@ -420,10 +416,6 @@ web-sys = "0.3.72"
[profile.dev.package.sqlx-macros]
opt-level = 3
[profile.release.package.nym-socks5-listener]
strip = true
codegen-units = 1
[profile.release.package.nym-client-wasm]
# lto = true
opt-level = 'z'
+7 -10
View File
@@ -14,6 +14,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
* `nym-cli` - a tool for interacting with the network from the CLI.
<!-- coming soon
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
-->
@@ -35,24 +36,20 @@ client ───► Gateway ──┘ mix │ mix ┌─►mix ───►
### Building
* Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html).
* Wallet build instructions are available on Nym [Technical docs](https://nymtech.net/docs/wallet/desktop-wallet.html).
* Wallet build instructions are available [here](https://github.com/nymtech/nym/tree/master/nym-wallet#installation-prerequisites---linux--mac).
### Developing
There's a [`sandbox.env`](https://github.com/nymtech/nym/envs/sandbox.env) file provided which you can rename to `.env` if you want convenient testing environment. Read more about sandbox environment in our [Operators Guide page](https://nymtech.net/operators/sandbox.html).
References for developers:
* [Developers Portal](https://nymtech.net/developers)
* [Typescript SDKs](https://sdk.nymtech.net/)
* [Technical Documentation - Nym network overview](https://nymtech.net/docs/)
* [Release Cycle - git flow](https://nymtech.net/operators/release-cycle.html)
* [Dev Docs](https://nymtech.net/docs/developers)
* [SDKs](https://nymtech.net/docs/developers/rust)
* [Network Docs](https://nymtech.net/docs/network)
* [Release Cycle - git flow](https://nymtech.net/docs/operators/release-cycle)
### Developer chat
You can chat to us in two places:
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
* The various developer channels on [Discord](https://nymtech.net/go/discord)
You can chat to us in the #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) or on the [Nym Forum](https://forum.nymtech.net).
### Tokenomics & Rewards
+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:
```
security@nymte.ch
security@nym.com
```
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
uid [ ultime ] Security Nym Technologies <security@nymte.ch>
sub rsa4096 2023-10-30 [E] [expire : 2026-10-29]
uid [ ultime ] Security Nym Technologies <security@nym.com>
ssb rsa4096/ACD0FBD79DC70ACC 2023-10-30 [E] [expire : 2026-10-29]
```
The fingerprint of the key is on the second line above.
If you need to chat __urgently__ to our team for a __critical__ security issue:
go to Matrix, and alert the core engineers with a private direct message:
Jedrzej Stuczynski @jstuczyn:nymtech.chat
Mark Sinclair @mark:nymtech.chat
Raphaël Walther @raphael:nymtech.chat
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
If you don't know what Matrix is, you can follow this documentation to create an account on this federation of instant messaging servers:
[Matrix for Instant Messaging](https://matrix.org/docs/chat_basics/matrix-for-im/)
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -48,43 +34,69 @@ vMFUIzBMHOPXH16036zGyFMC1esRd2qqil4b9KtLgCOkrD1VgpjcveoA0VyMJCN6
LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A
fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj
IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB
tC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW10ZS5jaD6J
AlQEEwEKAD4WIQQkslkugBpaqoZmyLp8PHJ/BQkFUAUCZT9elwIbAwUJBaOagAUL
CQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB8PHJ/BQkFUL7dD/9zO73uI5VR+SWx
PFmJW+9QsPiQbVRvGwNZurctmQ2s2Pe0vHRELFeqD5oYvSx2Lequ3Ir+zn/C3kDM
kNs40obSL6jCBiLPkxEY0JqzPM9jZr7EjvlibWV3f6DxooRIqEyfN57I3OBGlqZE
0Mx7sQuCcgau8C70DF952QhKUwXC2cmpmDKHVEEoio1xGSD4dQhGapCB32RQGtna
OGfAO9celNMvSq0Lp+aJxeACmWFY5T4/y79JPcT5vSs/yEIRmaH/fn2piwaFBsIq
gHJJMxO3740P1hF8j7KWUoUofuFaEALHBpEpjWTOj8ej1wmFlu+5F+jSVoc781Wb
ZZXu04cOBXnGTogzSxMpBe9TtLb28zd6WzFotC25KTI3pngMzXsQGLJLOwvoZKiS
LFjPRjg1rwobmB3Q3J2W5GYSveia0CDsZGP+g87GVVf/oD2Djpa68xyVYwIYeA6T
3DNdS77qHiRuGiS4kWXyVjDqOICboR4uCvt09zlkBuLDdTWqWYARUvZjtjs4w/Ol
rdrBI3A88ti8fRldYaNpu17ME1ilpN44yKoJtqiWc3Tisk8eYLfx6c7FQF3PrRva
mr7FZvhFsYML5CeNFHTEzN6Y3jjKN/60DvCfodWnWFK47Txkl8UAXGY2W9B0fWqQ
wUVr8uLuMyyMiKbeoufi7rGOj6AMErkCDQRlP16XARAA8FGmD5J3tM1BOM1niJxZ
JTdCauzEtxEoBL0RuqGBkR8U29sRM6DwuzjU7PwscFnBaGyU+eU73GwGkH3ozFfF
tllYhQrhP/kkN+0rEO5Xi+nR+4JCFRqrf3nJXAAPfiksURMp8er1dUOY2/e1ZSoL
tS+nzUivV8CfE+pgj/5YtGwPC+KYHLATkKkMELCrbW4UO06VWOqQsvr6kivXuJQQ
LdEAMpBlADmXFG45DmPKQzsBWUgvTwyGy3LX0nys8cgpex9BH8hhr01QmGyP469s
N3cNrtFuu8U6RAsiCD/8mlBuD3EQEU5SF0lc7kCICAZk+wElmXnimEi0TOYsbz6k
90lteicX70rA9GNeyI76H+VSOYvWpkRwaJAgUdzrAM1o9SHASq+cZ6nD85OZioQk
DWM6+Q+sf2oen0qJnnGmUr93kJIC0PIdgrXRrtiNfeRa1Z/H0LmREyyEMoFiVivn
z1vVk85Oq6Sf3ltUwvmDzuuJOtsp2Qp6+x6Snn/yKauI4uf4Cf/wKUch4r6Bwgg5
Dw49ky7lwlnALio4GIVoGLpLef93wWoDmp4Klyh3ZPf2nB0U91u3bHRUo7m+D7QJ
98cyKtqLLzjg7szGf60pIWNWRsadYQT3bSncynqknAjOV3BCvx6/ivsnpj//QjYR
HtviUAcQ1DBB6UC6q23FIs0AEQEAAYkCPAQYAQoAJhYhBCSyWS6AGlqqhmbIunw8
cn8FCQVQBQJlP16XAhsMBQkFo5qAAAoJEHw8cn8FCQVQzukP/iLxjOxT+UpPR//c
prDVSLkP4pF5bmw36U07jvqpS+/KTXsxiiQleffRabOpNLcd+K1ueavyt9nnIwHH
tHS9kM9A7DBw3LnpEbXki46QDCCI6niGijlLOEeAWqnocwMNTT05wVVgCtO3DQP2
MoSCcqHpXDChvOyr5d5xjYLVJhlctIMSomcVzGryjknPu0Yj/TkC/4c+m86ZWQUD
HqMHQIuiEenvb62/F4c5OJIRZPEn70wdddkgJuJU3eHdHrnuhCkjCC93GQGbGj03
Zqos6699y6hmPeD3U5IUv8ujwZYVCCuDm8gJfrp3R6WLfeZeK9WmTVBpCzsDg3fV
hSwmOk6pp8DAq1/Dev3yRkFggCEyGK6c9b+a0CRBncl8e5Q0QQIzNiS/uExQP3h+
ELJs3P0MLP+6FWhNUry09n3lnWkr1hY+v1M0GAxbfdv/tsCN1Pq/VQEz+CTqXqya
ftWldOHWw6Hh+gtwxcHjG4MBOrO5oICQ3lh2hGwQ58cDgZYSK/OGgJ9BggFl1CcM
0uGC0/TRCI1zt/4y+7efSZQMZkHo7VC/3MFbp2hcNejpW+BxVuwKTunFvWK3TLhq
sSlQ5yyhqchooepsFHq9bosKFjLJC01uprBv1rinoNduOy43FbyS7JPRRspANN0R
iC2pMbWdE0ZTQaFq6tPIg058pjqi
=nqgX
tCxTZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW0uY29tPokC
VAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJnSd5VAhsDBQkFo5qABQsJ
CAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQPPIP/ipGz2zLAjE2dSE3
VcqOvras0DfqIL9HDm26Dg6QO2D/4YRntw0RqVyuy+zFnRUm+RZCKLPLUzbQ9Wjb
G/Og5ttQVYQMu5eKu7OMvXkrbRo3teZFU+8IL08zIW6pyf9haxO6YMhLRy6cLYwW
0EYC6Qzn5gz3kI7VkI8fWfs2Dk4XEV3D+SVtBoF6KRxMXT6HZvpzoMSEJZBoNj8S
jw0TF8TFUQf49jUQbIHumukMswolrHi8a5ej8DSfNwSgz+Tt8oh5lu01kyUJiHn7
nuHaY4Y9cHUVAOSwq/hovG52+ZE1r3aiswvle/B19o9pKeWWVvacSptGxDQagBtQ
igoNLdRvY0XN2TEyX9pOHR0AoVOxtIW11CpkKuDbQG9vPwovqJ2L6+Fh3pzHYzcI
2GIShNm/Z2SZBiUqbljJe9H4UAT/aHgMINkEG8qzUKwO42MA5HJT7YbHTR17/QSF
Il5dhneRzmSbNcW2rdRwx/BmzrcsFJfqCt4JG/WDF293xSOjhFqQYvU4gCO+OB7o
KXjX907XXDjS2KEJ71OGqVfk/P7BqEfQNfrLtb02TyXJAPQXHhybv23c4E7zUs9V
lMjNizzxYB96uwJb0LAB2ijzEwoP91uGT2tFjk6F08x2QiArmXUdgrv44b39Stia
gJS0GYKqSzyr10xHhUuDA+GKYtcitC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVz
IDxzZWN1cml0eUBueW10ZS5jaD6JAjYEMAEKACAWIQQkslkugBpaqoZmyLp8PHJ/
BQkFUAUCZ0nftQIdIAAKCRB8PHJ/BQkFUFHDEACtyNuUEjKCLAT5mSfow85PjFgo
o8kHjQr/IIQ7ZbBOHeJJcrxDuypssiLh5XUjF3x5BiBfZ6vCxSb81RRwsDMp0mA1
qzv9G8sgW0HTQUnZ9oH6CYut2NgzAnQpmuacrunm9Zy0FJ3ejbmwUY/NqK6gJkle
66duHKhAy7DWjj7amd0C8bPDR+PA44fI3MezDHkQNaauKZTRqd1TqH8Qk5PAl4cB
o5gVzeZh/U7/usvtGhazAIUF5BqK6bTmDnYopg+2x8jjwrG4+08GrttZkNjBLXeA
Y/2U064yMz12LPv01qqAFdZ+coRy/ps/gOQTz34/VeW0CFy7TMqs4t3vSBWTqU7w
hnw/qj6cM33fdxctj6KDgJSCkZdx2fvwXgxiPqUa5+j9FlFBeD5RDAl6g6t8N1/K
Xca+zNYuSZgc297q1D+mtSD1C7uJNPxoAl+Bv5KNKpsjfQ+m04++CIFtGyX22aCA
h2/tHwQZIXhOiMAKOoupidDVDhgxtCJ3Ps416xL0sTZfsPfg+j1Uv/Em9pzPClEl
fX6+1O4DdSyZUQ4VsjMu/H5W/NQdbHgmqFrxQ6WX/0s5GMwO6GMDiPe8sOrwz9wD
WYtyjafxXOHEZ1OjYX5gr7bGaG4oKc2btTJN0B3Phg4dStnHCNjEYccxuV3507fj
HnNotkpXF2nGLxy+PYkCVAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJl
P16XAhsDBQkFo5qABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQ
vt0P/3M7ve4jlVH5JbE8WYlb71Cw+JBtVG8bA1m6ty2ZDazY97S8dEQsV6oPmhi9
LHYt6q7civ7Of8LeQMyQ2zjShtIvqMIGIs+TERjQmrM8z2NmvsSO+WJtZXd/oPGi
hEioTJ83nsjc4EaWpkTQzHuxC4JyBq7wLvQMX3nZCEpTBcLZyamYModUQSiKjXEZ
IPh1CEZqkIHfZFAa2do4Z8A71x6U0y9KrQun5onF4AKZYVjlPj/Lv0k9xPm9Kz/I
QhGZof9+famLBoUGwiqAckkzE7fvjQ/WEXyPspZShSh+4VoQAscGkSmNZM6Px6PX
CYWW77kX6NJWhzvzVZtlle7Thw4FecZOiDNLEykF71O0tvbzN3pbMWi0LbkpMjem
eAzNexAYsks7C+hkqJIsWM9GODWvChuYHdDcnZbkZhK96JrQIOxkY/6DzsZVV/+g
PYOOlrrzHJVjAhh4DpPcM11LvuoeJG4aJLiRZfJWMOo4gJuhHi4K+3T3OWQG4sN1
NapZgBFS9mO2OzjD86Wt2sEjcDzy2Lx9GV1ho2m7XswTWKWk3jjIqgm2qJZzdOKy
Tx5gt/HpzsVAXc+tG9qavsVm+EWxgwvkJ40UdMTM3pjeOMo3/rQO8J+h1adYUrjt
PGSXxQBcZjZb0HR9apDBRWvy4u4zLIyIpt6i5+LusY6PoAwSuQINBGU/XpcBEADw
UaYPkne0zUE4zWeInFklN0Jq7MS3ESgEvRG6oYGRHxTb2xEzoPC7ONTs/CxwWcFo
bJT55TvcbAaQfejMV8W2WViFCuE/+SQ37SsQ7leL6dH7gkIVGqt/eclcAA9+KSxR
Eynx6vV1Q5jb97VlKgu1L6fNSK9XwJ8T6mCP/li0bA8L4pgcsBOQqQwQsKttbhQ7
TpVY6pCy+vqSK9e4lBAt0QAykGUAOZcUbjkOY8pDOwFZSC9PDIbLctfSfKzxyCl7
H0EfyGGvTVCYbI/jr2w3dw2u0W67xTpECyIIP/yaUG4PcRARTlIXSVzuQIgIBmT7
ASWZeeKYSLRM5ixvPqT3SW16JxfvSsD0Y17Ijvof5VI5i9amRHBokCBR3OsAzWj1
IcBKr5xnqcPzk5mKhCQNYzr5D6x/ah6fSomecaZSv3eQkgLQ8h2CtdGu2I195FrV
n8fQuZETLIQygWJWK+fPW9WTzk6rpJ/eW1TC+YPO64k62ynZCnr7HpKef/Ipq4ji
5/gJ//ApRyHivoHCCDkPDj2TLuXCWcAuKjgYhWgYukt5/3fBagOangqXKHdk9/ac
HRT3W7dsdFSjub4PtAn3xzIq2osvOODuzMZ/rSkhY1ZGxp1hBPdtKdzKeqScCM5X
cEK/Hr+K+yemP/9CNhEe2+JQBxDUMEHpQLqrbcUizQARAQABiQI8BBgBCgAmFiEE
JLJZLoAaWqqGZsi6fDxyfwUJBVAFAmU/XpcCGwwFCQWjmoAACgkQfDxyfwUJBVDO
6Q/+IvGM7FP5Sk9H/9ymsNVIuQ/ikXlubDfpTTuO+qlL78pNezGKJCV599Fps6k0
tx34rW55q/K32ecjAce0dL2Qz0DsMHDcuekRteSLjpAMIIjqeIaKOUs4R4Baqehz
Aw1NPTnBVWAK07cNA/YyhIJyoelcMKG87Kvl3nGNgtUmGVy0gxKiZxXMavKOSc+7
RiP9OQL/hz6bzplZBQMeowdAi6IR6e9vrb8Xhzk4khFk8SfvTB112SAm4lTd4d0e
ue6EKSMIL3cZAZsaPTdmqizrr33LqGY94PdTkhS/y6PBlhUIK4ObyAl+undHpYt9
5l4r1aZNUGkLOwODd9WFLCY6TqmnwMCrX8N6/fJGQWCAITIYrpz1v5rQJEGdyXx7
lDRBAjM2JL+4TFA/eH4Qsmzc/Qws/7oVaE1SvLT2feWdaSvWFj6/UzQYDFt92/+2
wI3U+r9VATP4JOperJp+1aV04dbDoeH6C3DFweMbgwE6s7mggJDeWHaEbBDnxwOB
lhIr84aAn0GCAWXUJwzS4YLT9NEIjXO3/jL7t59JlAxmQejtUL/cwVunaFw16Olb
4HFW7ApO6cW9YrdMuGqxKVDnLKGpyGih6mwUer1uiwoWMskLTW6msG/WuKeg1247
LjcVvJLsk9FGykA03RGILakxtZ0TRlNBoWrq08iDTnymOqI=
=QPTf
-----END PGP PUBLIC KEY BLOCK-----
```
+1
View File
@@ -8,6 +8,7 @@ pub mod v3;
pub mod v4;
mod error;
mod util;
pub use error::Error;
pub use v4 as latest;
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub(crate) mod tests {
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
0, 0, 0, 0, 0, 1,
];
pub(crate) const RECIPIENT: &str = "CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f";
}
@@ -29,7 +29,7 @@ pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InitMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
@@ -41,7 +41,7 @@ impl InitMessage {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct FinalMessage {
/// Gateway client data
pub gateway_client: GatewayClient,
@@ -50,28 +50,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistrationData {
pub nonce: u64,
pub gateway_data: GatewayClient,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistredData {
pub pub_key: PeerPublicKey,
pub private_ip: IpAddr,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RemainingBandwidthData {
pub available_bandwidth: i64,
}
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct GatewayClient {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
@@ -147,7 +147,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac {
@@ -87,7 +87,7 @@ impl AuthenticatorRequest {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorRequestData {
Initial(InitMessage),
Final(Box<FinalMessage>),
@@ -100,28 +100,28 @@ impl AuthenticatorResponse {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse),
RemainingBandwidth(RemainingBandwidthResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PendingRegistrationResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistrationData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RemainingBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
@@ -19,6 +19,24 @@ impl From<v2::request::AuthenticatorRequest> for v3::request::AuthenticatorReque
}
}
impl TryFrom<v3::request::AuthenticatorRequest> for v2::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v3::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.try_into()?,
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
})
}
}
impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData {
fn from(authenticator_request_data: v2::request::AuthenticatorRequestData) -> Self {
match authenticator_request_data {
@@ -35,6 +53,29 @@ impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorR
}
}
impl TryFrom<v3::request::AuthenticatorRequestData> for v2::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v3::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v2::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v3::request::AuthenticatorRequestData::Final(gw_client) => Ok(
v2::request::AuthenticatorRequestData::Final(gw_client.into()),
),
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v3::request::AuthenticatorRequestData::TopUpBandwidth(_) => Err(
Self::Error::Conversion("no top up bandwidth variant in v2".to_string()),
),
}
}
}
impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
fn from(init_msg: v2::registration::InitMessage) -> Self {
Self {
@@ -43,6 +84,14 @@ impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
}
}
impl From<v3::registration::InitMessage> for v2::registration::InitMessage {
fn from(init_msg: v3::registration::InitMessage) -> Self {
Self {
pub_key: init_msg.pub_key,
}
}
}
impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMessage> {
fn from(gw_client: Box<v2::registration::FinalMessage>) -> Self {
Box::new(v3::registration::FinalMessage {
@@ -52,6 +101,15 @@ impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMe
}
}
impl From<Box<v3::registration::FinalMessage>> for Box<v2::registration::FinalMessage> {
fn from(gw_client: Box<v3::registration::FinalMessage>) -> Self {
Box::new(v2::registration::FinalMessage {
gateway_client: gw_client.gateway_client.into(),
credential: gw_client.credential,
})
}
}
impl From<v2::registration::GatewayClient> for v3::registration::GatewayClient {
fn from(gw_client: v2::registration::GatewayClient) -> Self {
Self {
@@ -93,7 +151,10 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
Ok(Self {
data: authenticator_response.data.try_into()?,
reply_to: authenticator_response.reply_to,
protocol: authenticator_response.protocol,
protocol: Protocol {
version: 2,
service_provider_type: authenticator_response.protocol.service_provider_type,
},
})
}
}
@@ -101,7 +162,10 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
impl From<v2::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse {
fn from(value: v2::response::AuthenticatorResponse) -> Self {
Self {
protocol: value.protocol,
protocol: Protocol {
version: 3,
service_provider_type: value.protocol.service_provider_type,
},
data: value.data.into(),
reply_to: value.reply_to,
}
@@ -270,3 +334,511 @@ impl From<v2::registration::RemainingBandwidthData> for v3::registration::Remain
}
}
}
#[cfg(test)]
mod tests {
use std::{net::IpAddr, str::FromStr};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use x25519_dalek::PublicKey;
use super::*;
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
#[test]
fn upgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v2::request::AuthenticatorRequest::new_initial_request(
v2::registration::InitMessage::new(pub_key),
reply_to,
);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
pub_key
})
);
}
#[test]
fn downgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
v3::registration::InitMessage::new(pub_key),
reply_to,
);
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::request::AuthenticatorRequestData::Initial(v2::registration::InitMessage {
pub_key
})
);
}
#[test]
fn upgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v2::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v2::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::Final(Box::new(
v3::registration::FinalMessage {
gateway_client: v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
credential
}
))
);
}
#[test]
fn downgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v3::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
let upgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v2::request::AuthenticatorRequestData::Final(Box::new(
v2::registration::FinalMessage {
gateway_client: v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
credential
}
))
);
}
#[test]
fn upgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v2::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_topup_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let top_up_message = v3::topup::TopUpMessage {
pub_key,
credential,
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
assert!(v2::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn upgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let registration_data = v2::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::PendingRegistration(
v3::response::PendingRegistrationResponse {
request_id,
reply_to,
reply: v3::registration::RegistrationData {
nonce,
gateway_data: v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
wg_port,
}
}
)
);
}
#[test]
fn downgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let registration_data = v3::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::PendingRegistration(
v2::response::PendingRegistrationResponse {
request_id,
reply_to,
reply: v2::registration::RegistrationData {
nonce,
gateway_data: v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
wg_port,
}
}
)
);
}
#[test]
fn upgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v2::registration::RegistredData {
pub_key,
private_ip,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
wg_port,
pub_key,
private_ip
}
})
);
}
#[test]
fn downgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
pub_key,
private_ip,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse {
request_id,
reply_to,
reply: v2::registration::RegistredData {
wg_port,
pub_key,
private_ip
}
})
);
}
#[test]
fn upgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v2::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::RemainingBandwidth(
v3::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::RemainingBandwidth(
v2::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v2::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_topup_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = v3::registration::RemainingBandwidthData {
available_bandwidth,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_topup_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
assert!(v2::response::AuthenticatorResponse::try_from(msg).is_err());
}
}
@@ -29,7 +29,7 @@ pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InitMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
@@ -41,7 +41,7 @@ impl InitMessage {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct FinalMessage {
/// Gateway client data
pub gateway_client: GatewayClient,
@@ -50,28 +50,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistrationData {
pub nonce: u64,
pub gateway_data: GatewayClient,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistredData {
pub pub_key: PeerPublicKey,
pub private_ip: IpAddr,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RemainingBandwidthData {
pub available_bandwidth: i64,
}
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct GatewayClient {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
@@ -147,7 +147,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac {
@@ -106,7 +106,7 @@ impl AuthenticatorRequest {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorRequestData {
Initial(InitMessage),
Final(Box<FinalMessage>),
@@ -120,7 +120,7 @@ impl AuthenticatorResponse {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse),
@@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData {
TopUpBandwidth(TopUpBandwidthResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PendingRegistrationResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistrationData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RemainingBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TopUpBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
@@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct TopUpMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
@@ -3,37 +3,82 @@
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use crate::{v2, v3, v4};
use crate::{v3, v4};
impl From<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest {
fn from(authenticator_request: v3::request::AuthenticatorRequest) -> Self {
Self {
impl TryFrom<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v3::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.into(),
data: authenticator_request.data.try_into()?,
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
})
}
}
impl TryFrom<v4::request::AuthenticatorRequest> for v3::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v4::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.try_into()?,
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
})
}
}
impl TryFrom<v3::request::AuthenticatorRequestData> for v4::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v3::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v4::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v3::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion(
"mac hash breaking change".to_string(),
)),
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
),
}
}
}
impl From<v3::request::AuthenticatorRequestData> for v4::request::AuthenticatorRequestData {
fn from(authenticator_request_data: v3::request::AuthenticatorRequestData) -> Self {
impl TryFrom<v4::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v4::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v3::request::AuthenticatorRequestData::Initial(init_msg) => {
v4::request::AuthenticatorRequestData::Initial(init_msg.into())
}
v3::request::AuthenticatorRequestData::Final(gw_client) => {
v4::request::AuthenticatorRequestData::Final(gw_client.into())
}
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => {
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
}
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into())
}
v4::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v3::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v4::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion(
"mac hash breaking change".to_string(),
)),
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
),
}
}
}
@@ -46,12 +91,11 @@ impl From<v3::registration::InitMessage> for v4::registration::InitMessage {
}
}
impl From<Box<v3::registration::FinalMessage>> for Box<v4::registration::FinalMessage> {
fn from(gw_client: Box<v3::registration::FinalMessage>) -> Self {
Box::new(v4::registration::FinalMessage {
gateway_client: gw_client.gateway_client.into(),
credential: gw_client.credential,
})
impl From<v4::registration::InitMessage> for v3::registration::InitMessage {
fn from(init_msg: v4::registration::InitMessage) -> Self {
Self {
pub_key: init_msg.pub_key,
}
}
}
@@ -64,67 +108,26 @@ impl From<Box<v3::topup::TopUpMessage>> for Box<v4::topup::TopUpMessage> {
}
}
impl From<v2::registration::GatewayClient> for v4::registration::GatewayClient {
fn from(gw_client: v2::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ips: gw_client.private_ip.into(),
mac: gw_client.mac.into(),
}
impl From<Box<v4::topup::TopUpMessage>> for Box<v3::topup::TopUpMessage> {
fn from(top_up_message: Box<v4::topup::TopUpMessage>) -> Self {
Box::new(v3::topup::TopUpMessage {
pub_key: top_up_message.pub_key,
credential: top_up_message.credential,
})
}
}
impl From<v3::registration::GatewayClient> for v4::registration::GatewayClient {
fn from(gw_client: v3::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ips: gw_client.private_ip.into(),
mac: gw_client.mac.into(),
}
}
}
impl From<v4::registration::GatewayClient> for v3::registration::GatewayClient {
fn from(gw_client: v4::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ip: gw_client.private_ips.ipv4.into(),
mac: gw_client.mac.into(),
}
}
}
impl From<v4::registration::GatewayClient> for v2::registration::GatewayClient {
fn from(gw_client: v4::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ip: gw_client.private_ips.ipv4.into(),
mac: gw_client.mac.into(),
}
}
}
impl From<v2::registration::ClientMac> for v4::registration::ClientMac {
fn from(mac: v2::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v3::registration::ClientMac> for v4::registration::ClientMac {
fn from(mac: v3::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v4::registration::ClientMac> for v3::registration::ClientMac {
fn from(mac: v4::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v4::registration::ClientMac> for v2::registration::ClientMac {
fn from(mac: v4::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
impl TryFrom<v3::response::AuthenticatorResponse> for v4::response::AuthenticatorResponse {
type Error = crate::Error;
fn try_from(value: v3::response::AuthenticatorResponse) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 4,
service_provider_type: value.protocol.service_provider_type,
},
data: value.data.try_into()?,
reply_to: value.reply_to,
})
}
}
@@ -137,11 +140,40 @@ impl TryFrom<v4::response::AuthenticatorResponse> for v3::response::Authenticato
Ok(Self {
data: authenticator_response.data.try_into()?,
reply_to: authenticator_response.reply_to,
protocol: authenticator_response.protocol,
protocol: Protocol {
version: 3,
service_provider_type: authenticator_response.protocol.service_provider_type,
},
})
}
}
impl TryFrom<v3::response::AuthenticatorResponseData> for v4::response::AuthenticatorResponseData {
type Error = crate::Error;
fn try_from(
authenticator_response_data: v3::response::AuthenticatorResponseData,
) -> Result<Self, Self::Error> {
match authenticator_response_data {
v3::response::AuthenticatorResponseData::PendingRegistration(_) => Err(
Self::Error::Conversion("mac hash breaking change".to_string()),
),
v3::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
v4::response::AuthenticatorResponseData::Registered(registered_response.into()),
),
v3::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Ok(v4::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response.into(),
)),
v3::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => Ok(
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into()),
),
}
}
}
impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData {
type Error = crate::Error;
@@ -149,13 +181,10 @@ impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::Authenti
authenticator_response_data: v4::response::AuthenticatorResponseData,
) -> Result<Self, Self::Error> {
match authenticator_response_data {
v4::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => Ok(
v3::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response.into(),
),
v4::response::AuthenticatorResponseData::PendingRegistration(_) => Err(
Self::Error::Conversion("mac hash breaking change".to_string()),
),
v4::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
v3::response::AuthenticatorResponseData::Registered(registered_response.into()),
),
@@ -173,8 +202,8 @@ impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::Authenti
}
}
impl From<v4::response::PendingRegistrationResponse> for v3::response::PendingRegistrationResponse {
fn from(value: v4::response::PendingRegistrationResponse) -> Self {
impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse {
fn from(value: v4::response::RegisteredResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
@@ -183,8 +212,8 @@ impl From<v4::response::PendingRegistrationResponse> for v3::response::PendingRe
}
}
impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse {
fn from(value: v4::response::RegisteredResponse) -> Self {
impl From<v3::response::RegisteredResponse> for v4::response::RegisteredResponse {
fn from(value: v3::response::RegisteredResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
@@ -193,6 +222,16 @@ impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse
}
}
impl From<v3::response::RemainingBandwidthResponse> for v4::response::RemainingBandwidthResponse {
fn from(value: v3::response::RemainingBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.map(Into::into),
}
}
}
impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse {
fn from(value: v4::response::RemainingBandwidthResponse) -> Self {
Self {
@@ -203,11 +242,31 @@ impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingB
}
}
impl From<v4::registration::RegistrationData> for v3::registration::RegistrationData {
fn from(value: v4::registration::RegistrationData) -> Self {
impl From<v3::response::TopUpBandwidthResponse> for v4::response::TopUpBandwidthResponse {
fn from(value: v3::response::TopUpBandwidthResponse) -> Self {
Self {
nonce: value.nonce,
gateway_data: value.gateway_data.into(),
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v4::response::TopUpBandwidthResponse> for v3::response::TopUpBandwidthResponse {
fn from(value: v4::response::TopUpBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
fn from(value: v3::registration::RegistredData) -> Self {
Self {
pub_key: value.pub_key,
private_ips: value.private_ip.into(),
wg_port: value.wg_port,
}
}
@@ -223,6 +282,14 @@ impl From<v4::registration::RegistredData> for v3::registration::RegistredData {
}
}
impl From<v3::registration::RemainingBandwidthData> for v4::registration::RemainingBandwidthData {
fn from(value: v3::registration::RemainingBandwidthData) -> Self {
Self {
available_bandwidth: value.available_bandwidth,
}
}
}
impl From<v4::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData {
fn from(value: v4::registration::RemainingBandwidthData) -> Self {
Self {
@@ -230,3 +297,441 @@ impl From<v4::registration::RemainingBandwidthData> for v3::registration::Remain
}
}
}
#[cfg(test)]
mod tests {
use std::{
net::{Ipv4Addr, Ipv6Addr},
str::FromStr,
};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use x25519_dalek::PublicKey;
use super::*;
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
#[test]
fn upgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
v3::registration::InitMessage::new(pub_key),
reply_to,
);
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::request::AuthenticatorRequestData::Initial(v4::registration::InitMessage {
pub_key
})
);
}
#[test]
fn downgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v4::request::AuthenticatorRequest::new_initial_request(
v4::registration::InitMessage::new(pub_key),
reply_to,
);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
pub_key
})
);
}
#[test]
fn upgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ipv4.into(),
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v3::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
assert!(v4::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn downgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let nonce = 42;
let gateway_client = v4::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ips,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v4::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v4::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
assert!(v3::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn upgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v4::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_topup_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let top_up_message = v4::topup::TopUpMessage {
pub_key,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v4::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::TopUpBandwidth(Box::new(
v3::topup::TopUpMessage {
pub_key,
credential
}
))
);
}
#[test]
fn upgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ipv4.into(),
nonce,
);
let registration_data = v3::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
assert!(v4::response::AuthenticatorResponse::try_from(msg).is_err());
}
#[test]
fn downgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let nonce = 42;
let wg_port = 51822;
let gateway_data = v4::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ips,
nonce,
);
let registration_data = v4::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
}
#[test]
fn upgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap());
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
pub_key,
private_ip: ipv4.into(),
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse {
request_id,
reply_to,
reply: v4::registration::RegistredData {
wg_port,
pub_key,
private_ips
}
})
);
}
#[test]
fn downgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let wg_port = 51822;
let registred_data = v4::registration::RegistredData {
pub_key,
private_ips,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
wg_port,
pub_key,
private_ip: ipv4.into()
}
})
);
}
#[test]
fn upgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::response::AuthenticatorResponseData::RemainingBandwidth(
v4::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v4::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v4::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::response::AuthenticatorResponseData::RemainingBandwidth(
v3::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_topup_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = v4::registration::RemainingBandwidthData {
available_bandwidth,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_topup_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
}
}
@@ -81,7 +81,7 @@ impl From<IpAddr> for IpPair {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InitMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
@@ -93,7 +93,7 @@ impl InitMessage {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct FinalMessage {
/// Gateway client data
pub gateway_client: GatewayClient,
@@ -102,28 +102,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistrationData {
pub nonce: u64,
pub gateway_data: GatewayClient,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistredData {
pub pub_key: PeerPublicKey,
pub private_ips: IpPair,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RemainingBandwidthData {
pub available_bandwidth: i64,
}
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct GatewayClient {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
@@ -199,7 +199,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac {
@@ -20,7 +20,7 @@ fn generate_random() -> u64 {
rng.next_u64()
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct AuthenticatorRequest {
pub protocol: Protocol,
pub data: AuthenticatorRequestData,
@@ -106,7 +106,7 @@ impl AuthenticatorRequest {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorRequestData {
Initial(InitMessage),
Final(Box<FinalMessage>),
@@ -10,7 +10,7 @@ use crate::make_bincode_serializer;
use super::VERSION;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct AuthenticatorResponse {
pub protocol: Protocol,
pub data: AuthenticatorResponseData,
@@ -120,7 +120,7 @@ impl AuthenticatorResponse {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse),
@@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData {
TopUpBandwidth(TopUpBandwidthResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PendingRegistrationResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistrationData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RemainingBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TopUpBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
@@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct TopUpMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
+1
View File
@@ -46,6 +46,7 @@ nym-sphinx = { path = "../nymsphinx" }
nym-statistics-common = { path = "../statistics" }
nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["serializable"] }
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" }
nym-credentials-interface = { path = "../credentials-interface" }
@@ -115,8 +115,13 @@ where
hardcoded_topology.get_gateways()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
.await?
crate::init::helpers::current_gateways(
&mut rng,
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
)
.await?
};
// since we're registering with a brand new gateway,
@@ -170,8 +170,13 @@ where
hardcoded_topology.get_gateways()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
.await?
crate::init::helpers::current_gateways(
&mut rng,
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
)
.await?
};
let gateway_setup = GatewaySetup::New {
@@ -14,7 +14,7 @@ use std::os::raw::c_int as RawFd;
use thiserror::Error;
#[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
#[derive(Debug, Error)]
@@ -170,7 +170,7 @@ pub struct LocalGateway {
// 'sender' part
/// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers.
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender,
// 'receiver' part
packet_router_tx: Option<oneshot::Sender<PacketRouter>>,
@@ -180,7 +180,7 @@ pub struct LocalGateway {
impl LocalGateway {
pub fn new(
local_identity: identity::PublicKey,
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender,
packet_router_tx: oneshot::Sender<PacketRouter>,
) -> Self {
LocalGateway {
@@ -208,8 +208,7 @@ mod nonwasm_sealed {
impl GatewaySender for LocalGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.packet_forwarder
.unbounded_send(packet)
.map_err(|err| err.into_send_error())
.forward_packet(packet)
.map_err(erase_err)
}
}
+9 -23
View File
@@ -7,7 +7,7 @@ use futures::{SinkExt, StreamExt};
use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient;
use nym_topology::{gateway, mix};
use nym_topology::gateway;
use nym_validator_client::client::IdentityKeyRef;
use nym_validator_client::UserAgent;
use rand::{seq::SliceRandom, Rng};
@@ -82,6 +82,7 @@ pub async fn current_gateways<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
user_agent: Option<UserAgent>,
minimum_performance: u8,
) -> Result<Vec<gateway::LegacyNode>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
@@ -95,41 +96,26 @@ pub async fn current_gateways<R: Rng>(
log::debug!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_all_basic_entry_assigned_nodes().await?;
log::debug!("Found {} gateways", gateways.len());
info!("nym api reports {} gateways", gateways.len());
log::trace!("Gateways: {:#?}", gateways);
let valid_gateways = gateways
.iter()
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<gateway::LegacyNode>>();
log::debug!("After checking validity: {}", valid_gateways.len());
log::trace!("Valid gateways: {:#?}", valid_gateways);
log::info!("nym-api reports {} valid gateways", valid_gateways.len());
log::info!(
"and {} after validity and performance filtering",
valid_gateways.len()
);
Ok(valid_gateways)
}
pub async fn current_mixnodes<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
) -> Result<Vec<mix::LegacyNode>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of mixnodes from: {nym_api}");
let mixnodes = client.get_all_basic_active_mixing_assigned_nodes().await?;
let valid_mixnodes = mixnodes
.iter()
.filter_map(|mixnode| mixnode.try_into().ok())
.collect::<Vec<mix::LegacyNode>>();
Ok(valid_mixnodes)
}
#[cfg(not(target_arch = "wasm32"))]
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
+8 -4
View File
@@ -9,10 +9,14 @@ license.workspace = true
[dependencies]
futures = { workspace = true }
log = { workspace = true }
tokio = { workspace = true, features = ["time", "net", "rt"] }
tokio-util = { workspace = true, features = ["codec"] }
tracing = { workspace = true }
tokio = { workspace = true, features = ["time"] }
tokio-util = { workspace = true, features = ["codec"], optional = true }
# internal
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::StreamExt;
use log::*;
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket;
@@ -18,13 +17,14 @@ use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::sleep;
use tokio_util::codec::Framed;
use tracing::*;
#[derive(Clone, Copy)]
pub struct Config {
initial_reconnection_backoff: Duration,
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
}
impl Config {
@@ -33,14 +33,12 @@ impl Config {
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
) -> Self {
Config {
initial_reconnection_backoff,
maximum_reconnection_backoff,
initial_connection_timeout,
maximum_connection_buffer_size,
use_legacy_version,
}
}
}
@@ -200,9 +198,8 @@ impl SendWithoutResponse for Client {
packet: NymPacket,
packet_type: PacketType,
) -> io::Result<()> {
trace!("Sending packet to {:?}", address);
let framed_packet =
FramedNymPacket::new(packet, packet_type, self.config.use_legacy_version);
trace!("Sending packet to {address:?}");
let framed_packet = FramedNymPacket::new(packet, packet_type);
if let Some(sender) = self.conn_new.get_mut(&address) {
if let Err(err) = sender.channel.try_send(framed_packet) {
@@ -260,7 +257,6 @@ mod tests {
maximum_reconnection_backoff: Duration::from_millis(300_000),
initial_connection_timeout: Duration::from_millis(1_500),
maximum_connection_buffer_size: 128,
use_legacy_version: false,
})
}
@@ -1,77 +1,72 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::{Client, Config, SendWithoutResponse};
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use futures::channel::mpsc::SendError;
use nym_sphinx::forwarding::packet::MixPacket;
use std::time::Duration;
use tokio::time::Instant;
pub type MixForwardingSender = mpsc::UnboundedSender<MixPacket>;
type MixForwardingReceiver = mpsc::UnboundedReceiver<MixPacket>;
/// 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,
pub fn mix_forwarding_channels() -> (MixForwardingSender, MixForwardingReceiver) {
let (tx, rx) = mpsc::unbounded();
(tx.into(), rx)
}
impl PacketForwarder {
pub fn new(
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,
);
#[derive(Clone)]
pub struct MixForwardingSender(mpsc::UnboundedSender<PacketToForward>);
let (packet_sender, packet_receiver) = mpsc::unbounded();
impl From<mpsc::UnboundedSender<PacketToForward>> for MixForwardingSender {
fn from(tx: mpsc::UnboundedSender<PacketToForward>) -> Self {
MixForwardingSender(tx)
}
}
(
PacketForwarder {
mixnet_client: Client::new(client_config),
packet_receiver,
shutdown,
},
packet_sender,
)
impl MixForwardingSender {
pub fn forward_packet(&self, packet: impl Into<PacketToForward>) -> Result<(), SendError> {
self.0
.unbounded_send(packet.into())
.map_err(|err| err.into_send_error())
}
pub async fn run(&mut self) {
while !self.shutdown.is_shutdown() {
tokio::select! {
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());
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.0.len()
}
}
let next_hop = mix_packet.next_hop();
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
pub type MixForwardingReceiver = mpsc::UnboundedReceiver<PacketToForward>;
if let Err(err) =
self.mixnet_client
.send_without_response(next_hop, packet, packet_type)
{
debug!("failed to forward the packet - {err}")
}
}
}
pub struct PacketToForward {
pub packet: MixPacket,
pub forward_delay_target: Option<Instant>,
}
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>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "client")]
pub mod client;
pub mod forwarder;
#[cfg(feature = "client")]
pub use client::{Client, Config, SendWithoutResponse};
+2
View File
@@ -48,6 +48,7 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-time = { path = "../../common/ecash-time" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
@@ -56,6 +57,7 @@ nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-credential-proxy-requests = { path = "../../nym-credential-proxy/nym-credential-proxy-requests" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -0,0 +1,41 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credentials_interface::{generate_keypair_user, generate_keypair_user_from_seed, Base58};
use serde::{Deserialize, Serialize};
use std::io::stdout;
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedKeys {
pub secret_key: String,
pub public_key: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Secret value that's used for deriving underlying ecash keypair
#[clap(long)]
pub(crate) bs58_encoded_client_secret: Option<String>,
}
pub fn generate_ecash_keypair(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let keypair = if let Some(secret) = args.bs58_encoded_client_secret {
let seed = bs58::decode(&secret).into_vec()?;
generate_keypair_user_from_seed(&seed)
} else {
generate_keypair_user()
};
let encoded = Bs58EncodedKeys {
secret_key: keypair.secret_key().to_bs58(),
public_key: keypair.public_key().to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod generate_keypair;
pub mod withdrawal_request;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct InternalEcash {
#[clap(subcommand)]
pub command: InternalEcashCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalEcashCommands {
/// Generate a dummy withdrawal request
GenerateWithdrawalRequest(withdrawal_request::Args),
/// Generate dummy ecash keypair
GenerateKeypair(generate_keypair::Args),
}
@@ -0,0 +1,78 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credential_proxy_requests::api::v1::ticketbook::models::TicketbookRequest;
use nym_credentials_interface::{
generate_keypair_user, withdrawal_request, Base58, SecretKeyUser, TicketType,
};
use nym_ecash_time::{ecash_default_expiration_date, EcashTime};
use serde::{Deserialize, Serialize};
use std::io::stdout;
use time::macros::format_description;
use time::Date;
use zeroize::Zeroizing;
fn parse_date(raw: &str) -> Result<Date, time::error::Parse> {
let format = format_description!("[year]-[month]-[day]");
Date::parse(raw, &format)
}
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedOutput {
pub ecash_proxy_request: TicketbookRequest,
pub ecash_secret: String,
/// Needed to later unblind shares
pub ecash_request_info_bs58: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Specify which type of ticketbook
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
pub(crate) ticketbook_type: TicketType,
/// Set expiration date for the ticketbook
#[clap(long, value_parser = parse_date, default_value_t = ecash_default_expiration_date())]
pub(crate) expiration_date: Date,
/// Provide ecash secret key (or generate a fresh one)
#[clap(long)]
pub(crate) ecash_secret_key_bs58: Option<String>,
}
pub async fn generate_withdrawal_request(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let ecash_keypair = if let Some(secret_key) = args.ecash_secret_key_bs58 {
let secret_key = Zeroizing::new(bs58::decode(Zeroizing::new(secret_key)).into_vec()?);
let sk = SecretKeyUser::from_bytes(&secret_key)?;
sk.into()
} else {
generate_keypair_user()
};
let (withdrawal_request, request_info) = withdrawal_request(
ecash_keypair.secret_key(),
args.expiration_date.ecash_unix_timestamp(),
args.ticketbook_type.encode(),
)?;
let encoded = Bs58EncodedOutput {
ecash_proxy_request: TicketbookRequest {
withdrawal_request: withdrawal_request.into(),
ecash_pubkey: ecash_keypair.public_key(),
expiration_date: args.expiration_date,
ticketbook_type: args.ticketbook_type,
is_freepass_request: false,
},
ecash_secret: ecash_keypair.secret_key().to_bs58(),
ecash_request_info_bs58: request_info.to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod ecash;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Internal {
#[clap(subcommand)]
pub command: InternalCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalCommands {
/// Ecash related internal commands
Ecash(ecash::InternalEcash),
}
+1
View File
@@ -3,5 +3,6 @@
pub mod context;
pub mod ecash;
pub mod internal;
pub mod utils;
pub mod validator;
@@ -13,6 +13,7 @@ cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw-storage-plus = { workspace = true }
schemars = { workspace = true }
utoipa = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
@@ -23,4 +24,5 @@ serde_json = { workspace = true }
vergen = { workspace = true, features = ["build", "git", "gitcl", "rustc", "cargo"] }
[features]
naive_float = []
naive_float = []
utoipa = ["dep:utoipa"]
@@ -221,6 +221,7 @@ fn default_unknown() -> String {
// TODO: there's no reason this couldn't be used for proper binaries, but in that case
// perhaps the struct should get renamed and moved to a "more" common crate
#[cw_serde]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct ContractBuildInformation {
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
#[serde(default = "default_unknown")]
@@ -0,0 +1,18 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Specification on how the active set should be updated.
*/
export type ActiveSetUpdate = {
/**
* The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
*/
entry_gateways: number,
/**
* The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`])
*/
exit_gateways: number,
/**
* The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`].
*/
mixnodes: number, };
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type GatewayConfigUpdate = { host: string, mix_port: number, clients_port: number, location: string, version: string, };
@@ -0,0 +1,30 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Specification of a rewarding interval.
*/
export type Interval = {
/**
* Monotonously increasing id of this interval.
*/
id: number,
/**
* Number of epochs in this interval.
*/
epochs_in_interval: number,
/**
* The timestamp indicating the start of the current rewarding epoch.
*/
current_epoch_start: string,
/**
* Monotonously increasing id of the current epoch in this interval.
*/
current_epoch_id: number,
/**
* The duration of all epochs in this interval.
*/
epoch_length: { secs: number; nanos: number; },
/**
* The total amount of elapsed epochs since the first epoch of the first interval.
*/
total_elapsed_epochs: number, };
@@ -0,0 +1,51 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Parameters required by the mix-mining reward distribution that do not change during an interval.
*/
export type IntervalRewardParams = {
/**
* Current value of the rewarding pool.
* It is expected to be constant throughout the interval.
*/
reward_pool: string,
/**
* Current value of the staking supply.
* It is expected to be constant throughout the interval.
*/
staking_supply: string,
/**
* Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set.
* Also known as `beta`.
*/
staking_supply_scale_factor: string,
/**
* Current value of the computed reward budget per epoch, per node.
* It is expected to be constant throughout the interval.
*/
epoch_reward_budget: string,
/**
* Current value of the stake saturation point.
* It is expected to be constant throughout the interval.
*/
stake_saturation_point: string,
/**
* Current value of the sybil resistance percent (`alpha`).
* It is not really expected to be changing very often.
* As a matter of fact, unless there's a very specific reason, it should remain constant.
*/
sybil_resistance: string,
/**
* Current active set work factor.
* It is not really expected to be changing very often.
* As a matter of fact, unless there's a very specific reason, it should remain constant.
*/
active_set_work_factor: string,
/**
* Current maximum interval pool emission.
* Assuming all nodes in the rewarded set are fully saturated and have 100% performance,
* this % of the reward pool would get distributed in rewards to all operators and its delegators.
* It is not really expected to be changing very often.
* As a matter of fact, unless there's a very specific reason, it should remain constant.
*/
interval_pool_emission: string, };
@@ -0,0 +1,35 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { RewardedSetParams } from "./RewardedSetParams";
/**
* Specification on how the rewarding params should be updated.
*/
export type IntervalRewardingParamsUpdate = {
/**
* Defines the new value of the reward pool.
*/
reward_pool: string | null,
/**
* Defines the new value of the staking supply.
*/
staking_supply: string | null,
/**
* Defines the new value of the staking supply scale factor.
*/
staking_supply_scale_factor: string | null,
/**
* Defines the new value of the sybil resistance percent.
*/
sybil_resistance_percent: string | null,
/**
* Defines the new value of the active set work factor.
*/
active_set_work_factor: string | null,
/**
* Defines the new value of the interval pool emission rate.
*/
interval_pool_emission: string | null,
/**
* Defines the parameters of the rewarded set.
*/
rewarded_set_params: RewardedSetParams | null, };
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type MixNodeConfigUpdate = { host: string, mix_port: number, verloc_port: number, http_api_port: number, version: string, };
@@ -0,0 +1,34 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.
*/
export type MixNode = {
/**
* Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com
*/
host: string,
/**
* Port used by this mixnode for listening for mix packets.
*/
mix_port: number,
/**
* Port used by this mixnode for listening for verloc requests.
*/
verloc_port: number,
/**
* Port used by this mixnode for its http(s) API
*/
http_api_port: number,
/**
* Base58-encoded x25519 public key used for sphinx key derivation.
*/
sphinx_key: string,
/**
* Base58-encoded ed25519 EdDSA public key.
*/
identity_key: string,
/**
* The self-reported semver version of this mixnode.
*/
version: string, };
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type NodeConfigUpdate = { host: string | null, custom_http_port: number | null, restore_default_http_port: boolean, };
@@ -0,0 +1,15 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Parameters used for rewarding particular node.
*/
export type NodeRewardingParameters = {
/**
* Performance of the particular node in the current epoch.
*/
performance: string,
/**
* Amount of work performed by this node in the current epoch
* also known as 'omega' in the paper
*/
work_factor: string, };
@@ -0,0 +1,20 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.
*/
export type NymNode = {
/**
* Network address of this nym-node, for example 1.1.1.1 or foo.mixnode.com
* that is used to discover other capabilities of this node.
*/
host: string,
/**
* Allow specifying custom port for accessing the http, and thus self-described, api
* of this node for the capabilities discovery.
*/
custom_http_port: number | null,
/**
* Base58-encoded ed25519 EdDSA public key.
*/
identity_key: string, };
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type PendingMixNodeChanges = { pledge_change: number | null, cost_params_change: number | null, };
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type PendingNodeChanges = { pledge_change: number | null, cost_params_change: number | null, };
@@ -0,0 +1,20 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type RewardEstimate = {
/**
* The amount of **decimal** coins that are going to get distributed to the node,
* i.e. the operator and all its delegators.
*/
total_node_reward: string,
/**
* The share of the reward that is going to get distributed to the node operator.
*/
operator: string,
/**
* The share of the reward that is going to get distributed among the node delegators.
*/
delegates: string,
/**
* The operating cost of this node. Note: it's already included in the operator reward.
*/
operating_cost: string, };
@@ -0,0 +1,19 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type RewardedSetParams = {
/**
* The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
*/
entry_gateways: number,
/**
* The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`])
*/
exit_gateways: number,
/**
* The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`].
*/
mixnodes: number,
/**
* Number of nodes in the 'standby' set. (i.e. [`Role::Standby`])
*/
standby: number, };
@@ -0,0 +1,12 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { IntervalRewardParams } from "./IntervalRewardParams";
import type { RewardedSetParams } from "./RewardedSetParams";
/**
* Parameters used for reward calculation.
*/
export type RewardingParams = {
/**
* Parameters that should remain unchanged throughout an interval.
*/
interval: IntervalRewardParams, rewarded_set: RewardedSetParams, };
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Role = "EntryGateway" | "Layer1" | "Layer2" | "Layer3" | "ExitGateway" | "Standby";
@@ -0,0 +1,23 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Basic information of a node that used to be part of the mix network but has already unbonded.
*/
export type UnbondedMixnode = {
/**
* Base58-encoded ed25519 EdDSA public key.
*/
identity_key: string,
/**
* Address of the owner of this mixnode.
*/
owner: string,
/**
* Entity who bonded this mixnode on behalf of the owner.
* If exists, it's most likely the address of the vesting contract.
*/
proxy: string | null,
/**
* Block height at which this mixnode has unbonded.
*/
unbonding_height: number, };
@@ -42,9 +42,11 @@ pub struct Gateway {
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct GatewayBond {
/// Original amount pledged by the operator of this node.
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
pub pledge_amount: Coin,
/// Address of the owner of this gateway.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub owner: Addr,
/// Block height at which this gateway has been bonded.
@@ -55,6 +57,7 @@ pub struct GatewayBond {
/// Entity who bonded this gateway on behalf of the owner.
/// If exists, it's most likely the address of the vesting contract.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub proxy: Option<Addr>,
}
@@ -81,20 +81,25 @@ impl MixNodeDetails {
// currently this struct is shared between mixnodes and nymnodes
#[cw_serde]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct NodeRewarding {
/// Information provided by the operator that influence the cost function.
pub cost_params: NodeCostParams,
/// Total pledge and compounded reward earned by the node operator.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub operator: Decimal,
/// Total delegation and compounded reward earned by all node delegators.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub delegates: Decimal,
/// Cumulative reward earned by the "unit delegation" since the block 0.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub total_unit_reward: Decimal,
/// Value of the theoretical "unit delegation" that has delegated to this node at block 0.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub unit_delegation: Decimal,
/// Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt
@@ -491,14 +496,17 @@ impl NodeRewarding {
::cosmwasm_schema::schemars::JsonSchema,
)]
#[schemars(crate = "::cosmwasm_schema::schemars")]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct MixNodeBond {
/// Unique id assigned to the bonded mixnode.
pub mix_id: NodeId,
/// Address of the owner of this mixnode.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub owner: Addr,
/// Original amount pledged by the operator of this node.
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
pub original_pledge: Coin,
// REMOVED (but might be needed due to legacy things, idk yet)
@@ -509,6 +517,7 @@ pub struct MixNodeBond {
/// Entity who bonded this mixnode on behalf of the owner.
/// If exists, it's most likely the address of the vesting contract.
#[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
pub proxy: Option<Addr>,
/// Block height at which this mixnode has been bonded.
@@ -544,6 +553,7 @@ impl MixNodeBond {
feature = "generate-ts",
ts(export, export_to = "ts-packages/types/src/types/rust/Mixnode.ts")
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct MixNode {
/// Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com
pub host: String,
@@ -570,11 +580,14 @@ pub struct MixNode {
/// The cost parameters, or the cost function, defined for the particular mixnode that influences
/// how the rewards should be split between the node operator and its delegators.
#[cw_serde]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct NodeCostParams {
/// The profit margin of the associated node, i.e. the desired percent of the reward to be distributed to the operator.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub profit_margin_percent: Percent,
/// Operating cost of the associated node per the entire interval.
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
pub interval_operating_cost: Coin,
}
@@ -669,7 +682,9 @@ pub struct PendingMixNodeChanges {
}
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct LegacyPendingMixNodeChanges {
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
pub pledge_change: Option<EpochEventId>,
}
@@ -231,6 +231,7 @@ pub struct RoleMetadata {
/// Full details associated with given node.
#[cw_serde]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct NymNodeDetails {
/// Basic bond information of this node, such as owner address, original pledge, etc.
pub bond_information: NymNodeBond,
@@ -288,14 +289,19 @@ impl NymNodeDetails {
}
#[cw_serde]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct NymNodeBond {
/// Unique id assigned to the bonded node.
#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
pub node_id: NodeId,
/// Address of the owner of this nym-node.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub owner: Addr,
/// Original amount pledged by the operator of this node.
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
pub original_pledge: Coin,
/// Block height at which this nym-node has been bonded.
@@ -348,6 +354,7 @@ impl NymNodeBond {
feature = "generate-ts",
ts(export, export_to = "ts-packages/types/src/types/rust/NymNode.ts")
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct NymNode {
/// Network address of this nym-node, for example 1.1.1.1 or foo.mixnode.com
/// that is used to discover other capabilities of this node.
@@ -358,6 +365,7 @@ pub struct NymNode {
pub custom_http_port: Option<u16>,
/// Base58-encoded ed25519 EdDSA public key.
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub identity_key: IdentityKey,
// TODO: I don't think we want to include sphinx keys here,
// given we want to rotate them and keeping that in sync with contract will be a PITA
@@ -435,8 +443,11 @@ pub struct NodeConfigUpdate {
export_to = "ts-packages/types/src/types/rust/PendingNodeChanges.ts"
)
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct PendingNodeChanges {
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
pub pledge_change: Option<EpochEventId>,
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
pub cost_params_change: Option<IntervalEventId>,
}
@@ -21,31 +21,37 @@ pub type WorkFactor = Decimal;
)]
#[cw_serde]
#[derive(Copy)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct IntervalRewardParams {
/// Current value of the rewarding pool.
/// It is expected to be constant throughout the interval.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub reward_pool: Decimal,
/// Current value of the staking supply.
/// It is expected to be constant throughout the interval.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub staking_supply: Decimal,
/// Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set.
/// Also known as `beta`.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub staking_supply_scale_factor: Percent,
// computed values
/// Current value of the computed reward budget per epoch, per node.
/// It is expected to be constant throughout the interval.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub epoch_reward_budget: Decimal,
/// Current value of the stake saturation point.
/// It is expected to be constant throughout the interval.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub stake_saturation_point: Decimal,
// constants(-ish)
@@ -54,6 +60,7 @@ pub struct IntervalRewardParams {
/// It is not really expected to be changing very often.
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub sybil_resistance: Percent,
// default: 10
@@ -61,6 +68,7 @@ pub struct IntervalRewardParams {
/// It is not really expected to be changing very often.
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub active_set_work_factor: Decimal,
// default: 2%
@@ -70,6 +78,7 @@ pub struct IntervalRewardParams {
/// It is not really expected to be changing very often.
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub interval_pool_emission: Percent,
}
@@ -90,6 +99,7 @@ impl IntervalRewardParams {
)]
#[cw_serde]
#[derive(Copy)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct RewardingParams {
/// Parameters that should remain unchanged throughout an interval.
pub interval: IntervalRewardParams,
@@ -254,6 +264,7 @@ impl RewardingParams {
)]
#[cw_serde]
#[derive(Copy)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct RewardedSetParams {
/// The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
pub entry_gateways: u32,
@@ -17,10 +17,12 @@ pub mod simulator;
)]
#[cw_serde]
#[derive(Copy, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct RewardEstimate {
/// The amount of **decimal** coins that are going to get distributed to the node,
/// i.e. the operator and all its delegators.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub total_node_reward: Decimal,
// note that operator reward includes the operating_cost,
@@ -28,14 +30,17 @@ pub struct RewardEstimate {
// in that case the operator reward would still be `1nym` as opposed to 0
/// The share of the reward that is going to get distributed to the node operator.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub operator: Decimal,
/// The share of the reward that is going to get distributed among the node delegators.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub delegates: Decimal,
/// The operating cost of this node. Note: it's already included in the operator reward.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
pub operating_cost: Decimal,
}
@@ -134,6 +134,14 @@ where
}
}
#[cfg(feature = "utoipa")]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "utoipa", schema(title = "Coin"))]
pub struct CoinSchema {
pub denom: String,
pub amount: String,
}
/// The current state of the mixnet contract.
#[cw_serde]
pub struct ContractState {
@@ -0,0 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* The vesting period.
*/
export type Period = "Before" | { "In": number } | "After";
@@ -7,7 +7,7 @@ use crate::ClientBandwidth;
use nym_credentials::ecash::utils::ecash_today;
use nym_credentials_interface::Bandwidth;
use nym_gateway_requests::ServerResponse;
use nym_gateway_storage::Storage;
use nym_gateway_storage::GatewayStorage;
use si_scale::helpers::bibytes2;
use time::OffsetDateTime;
use tracing::*;
@@ -15,17 +15,17 @@ use tracing::*;
const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB
#[derive(Clone)]
pub struct BandwidthStorageManager<S> {
pub(crate) storage: S,
pub struct BandwidthStorageManager {
pub(crate) storage: GatewayStorage,
pub(crate) client_bandwidth: ClientBandwidth,
pub(crate) client_id: i64,
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
pub(crate) only_coconut_credentials: bool,
}
impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
impl BandwidthStorageManager {
pub fn new(
storage: S,
storage: GatewayStorage,
client_bandwidth: ClientBandwidth,
client_id: i64,
bandwidth_cfg: BandwidthFlushingBehaviourConfig,
@@ -13,7 +13,6 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
use nym_credentials_interface::Bandwidth;
use nym_credentials_interface::{ClientTicket, TicketType};
use nym_gateway_storage::Storage;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
@@ -126,21 +125,18 @@ pub struct CredentialHandlerConfig {
pub maximum_time_between_redemption: Duration,
}
pub(crate) struct CredentialHandler<St: Storage> {
pub(crate) struct CredentialHandler {
config: CredentialHandlerConfig,
multisig_threshold: f32,
ticket_receiver: UnboundedReceiver<ClientTicket>,
shared_state: SharedState<St>,
shared_state: SharedState,
pending_tickets: Vec<PendingVerification>,
pending_redemptions: Vec<PendingRedemptionVote>,
}
impl<St> CredentialHandler<St>
where
St: Storage + Clone + 'static,
{
impl CredentialHandler {
async fn rebuild_pending_tickets(
shared_state: &SharedState<St>,
shared_state: &SharedState,
) -> Result<Vec<PendingVerification>, EcashTicketError> {
// 1. get all tickets that were not fully verified
let unverified = shared_state.storage.get_all_unverified_tickets().await?;
@@ -188,7 +184,7 @@ where
}
async fn rebuild_pending_votes(
shared_state: &SharedState<St>,
shared_state: &SharedState,
) -> Result<Vec<PendingRedemptionVote>, EcashTicketError> {
// 1. get all tickets that were not fully verified
let unverified = shared_state.storage.get_all_unresolved_proposals().await?;
@@ -259,7 +255,7 @@ where
pub(crate) async fn new(
config: CredentialHandlerConfig,
ticket_receiver: UnboundedReceiver<ClientTicket>,
shared_state: SharedState<St>,
shared_state: SharedState,
) -> Result<Self, Error> {
let multisig_threshold = shared_state
.nyxd_client
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// 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::nym_api::EpochId;
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}")]
InternalStorageFailure {
#[from]
source: StorageError,
source: GatewayStorageError,
},
#[error("failed to create ticket redemption proposal: {source}")]
@@ -8,7 +8,7 @@ use error::EcashTicketError;
use futures::channel::mpsc::{self, UnboundedSender};
use nym_credentials::CredentialSpendingData;
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::DirectSigningHttpRpcNyxdClient;
use state::SharedState;
@@ -23,24 +23,21 @@ mod state;
pub const TIME_RANGE_SEC: i64 = 30;
pub struct EcashManager<S> {
shared_state: SharedState<S>,
pub struct EcashManager {
shared_state: SharedState,
pk_bytes: [u8; 32], // bytes representation of a pub key representing the verifier
pay_infos: Mutex<Vec<NymPayInfo>>,
cred_sender: UnboundedSender<ClientTicket>,
}
impl<S> EcashManager<S>
where
S: Storage + Clone + 'static,
{
impl EcashManager {
pub async fn new(
credential_handler_cfg: CredentialHandlerConfig,
nyxd_client: DirectSigningHttpRpcNyxdClient,
pk_bytes: [u8; 32],
shutdown: nym_task::TaskClient,
storage: S,
storage: GatewayStorage,
) -> Result<Self, Error> {
let shared_state = SharedState::new(nyxd_client, storage).await?;
@@ -66,7 +63,7 @@ where
self.shared_state.verification_key(epoch_id).await
}
pub fn storage(&self) -> &S {
pub fn storage(&self) -> &GatewayStorage {
&self.shared_state.storage
}
@@ -6,7 +6,7 @@ use crate::Error;
use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg};
use nym_credentials_interface::VerificationKeyAuth;
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::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{
@@ -23,20 +23,17 @@ use tracing::{error, trace, warn};
// state shared by different subtasks dealing with credentials
#[derive(Clone)]
pub(crate) struct SharedState<S> {
pub(crate) struct SharedState {
pub(crate) nyxd_client: Arc<RwLock<DirectSigningHttpRpcNyxdClient>>,
pub(crate) address: AccountId,
pub(crate) epoch_data: Arc<RwLock<BTreeMap<EpochId, EpochState>>>,
pub(crate) storage: S,
pub(crate) storage: GatewayStorage,
}
impl<S> SharedState<S>
where
S: Storage + Clone,
{
impl SharedState {
pub(crate) async fn new(
nyxd_client: DirectSigningHttpRpcNyxdClient,
storage: S,
storage: GatewayStorage,
) -> Result<Self, Error> {
let address = nyxd_client.address();
+1 -1
View File
@@ -39,7 +39,7 @@ pub enum Error {
OutOfBandwidth { required: i64, available: i64 },
#[error("Internal gateway storage error")]
StorageError(#[from] nym_gateway_storage::error::StorageError),
StorageError(#[from] nym_gateway_storage::error::GatewayStorageError),
#[error("{0}")]
UnknownTicketType(#[from] nym_credentials_interface::UnknownTicketType),
+10 -12
View File
@@ -2,17 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
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 time::{Date, OffsetDateTime};
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::*;
use ecash::EcashManager;
pub use error::*;
pub mod bandwidth_storage_manager;
@@ -20,17 +18,17 @@ mod client_bandwidth;
pub mod ecash;
pub mod error;
pub struct CredentialVerifier<S> {
pub struct CredentialVerifier {
credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager<S>>,
bandwidth_storage_manager: BandwidthStorageManager<S>,
ecash_verifier: Arc<EcashManager>,
bandwidth_storage_manager: BandwidthStorageManager,
}
impl<S: Storage + Clone + 'static> CredentialVerifier<S> {
impl CredentialVerifier {
pub fn new(
credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager<S>>,
bandwidth_storage_manager: BandwidthStorageManager<S>,
ecash_verifier: Arc<EcashManager>,
bandwidth_storage_manager: BandwidthStorageManager,
) -> Self {
CredentialVerifier {
credential,
+1
View File
@@ -16,6 +16,7 @@ serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
strum = { workspace = true, features = ["derive"] }
time = { workspace = true, features = ["serde"] }
utoipa = { workspace = true }
rand = { workspace = true }
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
+2
View File
@@ -225,8 +225,10 @@ impl From<PayInfo> for NymPayInfo {
Clone,
Debug,
PartialEq,
Eq,
Serialize,
Deserialize,
Hash,
strum::Display,
strum::EnumString,
strum::EnumIter,
@@ -86,7 +86,6 @@ impl Display for AddressPolicyAction {
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "openapi", aliases(ExitPolicy))]
pub struct AddressPolicy {
/// A list of rules to apply to find out whether an address is
/// contained by this policy.
@@ -727,10 +726,10 @@ mod test {
let policy = AddressPolicy::parse_from_torrc(
r#"
ExitPolicy reject 1.2.3.4/32:*
ExitPolicy reject 1.2.3.5:*
ExitPolicy reject 1.2.3.5:*
ExitPolicy reject 1.2.3.6/16:*
ExitPolicy reject 1.2.3.6/16:123-456
ExitPolicy accept *:53
ExitPolicy reject 1.2.3.6/16:123-456
ExitPolicy accept *:53
ExitPolicy accept6 *6:119
ExitPolicy accept *4:120
ExitPolicy reject6 [FC00::]/7:*
+3
View File
@@ -16,12 +16,15 @@ sqlx = { workspace = true, features = [
"migrate",
"time",
] }
strum = { workspace = true }
time = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
nym-sphinx = { path = "../nymsphinx" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-node-metrics = { path = "../../nym-node/nym-node-metrics" }
nym-statistics-common = { path = "../statistics" }
[build-dependencies]
+6 -8
View File
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: GPL-3.0-only
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 sessions::SessionManager;
use sqlx::ConnectOptions;
@@ -70,8 +71,8 @@ impl PersistentStatsStorage {
.session_manager
.insert_finished_session(
date,
session.duration.whole_milliseconds() as i64,
session.typ.to_string().into(),
session.duration.as_millis() as i64,
session.typ.to_string(),
)
.await?)
}
@@ -125,7 +126,7 @@ impl PersistentStatsStorage {
.insert_active_session(
client_address.as_base58_string(),
session.start,
session.typ.to_string().into(),
session.typ.to_string(),
)
.await?)
}
@@ -137,10 +138,7 @@ impl PersistentStatsStorage {
) -> Result<(), StatsStorageError> {
Ok(self
.session_manager
.update_active_session_type(
client_address.as_base58_string(),
session_type.to_string().into(),
)
.update_active_session_type(client_address.as_base58_string(), session_type.to_string())
.await?)
}
+18 -74
View File
@@ -1,9 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// 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 time::{Duration, OffsetDateTime};
use time::OffsetDateTime;
pub use nym_credentials_interface::TicketType;
#[derive(FromRow)]
pub struct StoredFinishedSession {
@@ -11,52 +13,26 @@ pub struct StoredFinishedSession {
typ: String,
}
impl StoredFinishedSession {
pub fn serialize(&self) -> (u64, String) {
(
self.duration_ms as u64, //we are sure that it fits in a u64, see `fn end_at`
self.typ.clone(),
)
impl From<StoredFinishedSession> for FinishedSession {
fn from(value: StoredFinishedSession) -> Self {
FinishedSession {
duration: std::time::Duration::from_millis(value.duration_ms as u64),
typ: SessionType::from_string(value.typ),
}
}
}
pub struct FinishedSession {
pub duration: Duration,
pub typ: SessionType,
pub trait ToSessionType {
fn to_session_type(&self) -> SessionType;
}
#[derive(PartialEq)]
pub enum SessionType {
Vpn,
Mixnet,
Unknown,
}
impl SessionType {
pub fn to_string(&self) -> &str {
impl ToSessionType for TicketType {
fn to_session_type(&self) -> SessionType {
match self {
Self::Vpn => "vpn",
Self::Mixnet => "mixnet",
Self::Unknown => "unknown",
}
}
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,
TicketType::V1MixnetEntry => SessionType::Mixnet,
TicketType::V1MixnetExit => SessionType::Mixnet,
TicketType::V1WireguardEntry => SessionType::Vpn,
TicketType::V1WireguardExit => SessionType::Vpn,
}
}
}
@@ -67,38 +43,6 @@ pub(crate) struct StoredActiveSession {
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 {
fn from(value: StoredActiveSession) -> Self {
ActiveSession {
-1
View File
@@ -9,7 +9,6 @@ edition.workspace = true
license.workspace = true
[dependencies]
async-trait = { workspace = true }
bincode = { workspace = true }
defguard_wireguard_rs = { workspace = true }
log = { workspace = true }
+1 -1
View File
@@ -4,7 +4,7 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum StorageError {
pub enum GatewayStorageError {
#[error("Database experienced an internal error: {0}")]
InternalDatabaseError(#[from] sqlx::Error),
+95 -287
View File
@@ -1,10 +1,8 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use async_trait::async_trait;
use bandwidth::BandwidthManager;
use clients::{ClientManager, ClientType};
use error::StorageError;
use inboxes::InboxManager;
use models::{
Client, PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage,
@@ -29,237 +27,11 @@ mod shared_keys;
mod tickets;
mod wireguard_peers;
#[async_trait]
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>;
}
pub use error::GatewayStorageError;
// note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)]
pub struct PersistentStorage {
pub struct GatewayStorage {
client_manager: ClientManager,
shared_key_manager: SharedKeysManager,
inbox_manager: InboxManager,
@@ -268,7 +40,7 @@ pub struct PersistentStorage {
wireguard_peer_manager: wireguard_peers::WgPeerManager,
}
impl PersistentStorage {
impl GatewayStorage {
/// Initialises `PersistentStorage` using the provided path.
///
/// # Arguments
@@ -278,7 +50,7 @@ impl PersistentStorage {
pub async fn init<P: AsRef<Path> + Send>(
database_path: P,
message_retrieval_limit: i64,
) -> Result<Self, StorageError> {
) -> Result<Self, GatewayStorageError> {
debug!(
"Attempting to connect to database {:?}",
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
Ok(PersistentStorage {
Ok(GatewayStorage {
client_manager: clients::ClientManager::new(connection_pool.clone()),
wireguard_peer_manager: wireguard_peers::WgPeerManager::new(connection_pool.clone()),
shared_key_manager: SharedKeysManager::new(connection_pool.clone()),
@@ -318,23 +90,22 @@ impl PersistentStorage {
}
}
#[async_trait]
impl Storage for PersistentStorage {
async fn get_mixnet_client_id(
impl GatewayStorage {
pub async fn get_mixnet_client_id(
&self,
client_address: DestinationAddressBytes,
) -> Result<i64, StorageError> {
) -> Result<i64, GatewayStorageError> {
Ok(self
.shared_key_manager
.client_id(&client_address.as_base58_string())
.await?)
}
async fn insert_shared_keys(
pub async fn insert_shared_keys(
&self,
client_address: DestinationAddressBytes,
shared_keys: &SharedGatewayKey,
) -> Result<i64, StorageError> {
) -> Result<i64, GatewayStorageError> {
let client_address_bs58 = client_address.as_base58_string();
let client_id = match self
.shared_key_manager
@@ -359,10 +130,10 @@ impl Storage for PersistentStorage {
Ok(client_id)
}
async fn get_shared_keys(
pub async fn get_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<Option<PersistedSharedKeys>, StorageError> {
) -> Result<Option<PersistedSharedKeys>, GatewayStorageError> {
let keys = self
.shared_key_manager
.get_shared_keys(&client_address.as_base58_string())
@@ -371,37 +142,37 @@ impl Storage for PersistentStorage {
}
#[allow(dead_code)]
async fn remove_shared_keys(
pub async fn remove_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
self.shared_key_manager
.remove_shared_keys(&client_address.as_base58_string())
.await?;
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?;
Ok(client)
}
async fn store_message(
pub async fn store_message(
&self,
client_address: DestinationAddressBytes,
message: Vec<u8>,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
self.inbox_manager
.insert_message(&client_address.as_base58_string(), message)
.await?;
Ok(())
}
async fn retrieve_messages(
pub async fn retrieve_messages(
&self,
client_address: DestinationAddressBytes,
start_after: Option<i64>,
) -> Result<(Vec<StoredMessage>, Option<i64>), StorageError> {
) -> Result<(Vec<StoredMessage>, Option<i64>), GatewayStorageError> {
let messages = self
.inbox_manager
.get_messages(&client_address.as_base58_string(), start_after)
@@ -409,87 +180,95 @@ impl Storage for PersistentStorage {
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 {
self.inbox_manager.remove_message(id).await?;
}
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?;
Ok(())
}
async fn set_expiration(
pub async fn set_expiration(
&self,
client_id: i64,
expiration: OffsetDateTime,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
self.bandwidth_manager
.set_expiration(client_id, expiration)
.await?;
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?;
Ok(())
}
async fn get_available_bandwidth(
pub async fn get_available_bandwidth(
&self,
client_id: i64,
) -> Result<Option<PersistedBandwidth>, StorageError> {
) -> Result<Option<PersistedBandwidth>, GatewayStorageError> {
Ok(self
.bandwidth_manager
.get_available_bandwidth(client_id)
.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
.bandwidth_manager
.increase_bandwidth(client_id, amount)
.await?)
}
async fn revoke_ticket_bandwidth(
pub async fn revoke_ticket_bandwidth(
&self,
ticket_id: i64,
amount: i64,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
Ok(self
.bandwidth_manager
.revoke_ticket_bandwidth(ticket_id, amount)
.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
.bandwidth_manager
.decrease_bandwidth(client_id, amount)
.await?)
}
async fn insert_epoch_signers(
pub async fn insert_epoch_signers(
&self,
epoch_id: i64,
signer_ids: Vec<i64>,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
self.ticket_manager
.insert_ecash_signers(epoch_id, signer_ids)
.await?;
Ok(())
}
async fn insert_received_ticket(
pub async fn insert_received_ticket(
&self,
client_id: i64,
received_at: OffsetDateTime,
serial_number: 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,
// but nothing too tragic. we just won't get paid for a single ticket
let ticket_id = self
@@ -503,24 +282,24 @@ impl Storage for PersistentStorage {
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?)
}
async fn insert_ticket_verification(
pub async fn insert_ticket_verification(
&self,
ticket_id: i64,
signer_id: i64,
verified_at: OffsetDateTime,
accepted: bool,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
self.ticket_manager
.insert_ticket_verification(ticket_id, signer_id, verified_at, accepted)
.await?;
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
self.ticket_manager.set_rejected_ticket(ticket_id).await?;
@@ -531,7 +310,7 @@ impl Storage for PersistentStorage {
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
self.ticket_manager
.insert_verified_ticket(ticket_id)
@@ -545,36 +324,41 @@ impl Storage for PersistentStorage {
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
.remove_binary_ticket_data(ticket_id)
.await?;
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
.ticket_manager
.get_all_verified_tickets_with_sn()
.await?)
}
async fn get_all_proposed_tickets_with_sn(
pub async fn get_all_proposed_tickets_with_sn(
&self,
proposal_id: u32,
) -> Result<Vec<VerifiedTicket>, StorageError> {
) -> Result<Vec<VerifiedTicket>, GatewayStorageError> {
Ok(self
.ticket_manager
.get_all_proposed_tickets_with_sn(proposal_id as i64)
.await?)
}
async fn insert_redemption_proposal(
pub async fn insert_redemption_proposal(
&self,
tickets: &[VerifiedTicket],
proposal_id: u32,
created_at: OffsetDateTime,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
// if we crash between those, there might a bit of an issue. we should revisit it later
// 1. insert the actual proposal
@@ -592,12 +376,12 @@ impl Storage for PersistentStorage {
Ok(())
}
async fn clear_post_proposal_data(
pub async fn clear_post_proposal_data(
&self,
proposal_id: u32,
resolved_at: OffsetDateTime,
rejected: bool,
) -> Result<(), StorageError> {
) -> Result<(), GatewayStorageError> {
// 1. update proposal metadata
self.ticket_manager
.update_redemption_proposal(proposal_id as i64, resolved_at, rejected)
@@ -616,11 +400,13 @@ impl Storage for PersistentStorage {
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?)
}
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
.get_unverified_tickets()
.await?
@@ -629,29 +415,37 @@ impl Storage for PersistentStorage {
.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
.ticket_manager
.get_all_unresolved_redemption_proposal_ids()
.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
.ticket_manager
.get_verification_votes(ticket_id)
.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?)
}
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,
peer: &defguard_wireguard_rs::host::Peer,
with_client_id: bool,
) -> Result<Option<i64>, StorageError> {
) -> Result<Option<i64>, GatewayStorageError> {
let client_id = match self
.wireguard_peer_manager
.retrieve_peer(&peer.public_key.to_string())
@@ -676,10 +470,15 @@ impl Storage for PersistentStorage {
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,
peer_public_key: &str,
) -> Result<Option<WireguardPeer>, StorageError> {
) -> Result<Option<WireguardPeer>, GatewayStorageError> {
let peer = self
.wireguard_peer_manager
.retrieve_peer(peer_public_key)
@@ -687,12 +486,21 @@ impl Storage for PersistentStorage {
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?;
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
.remove_peer(peer_public_key)
.await?;
+8 -8
View File
@@ -1,7 +1,7 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::StorageError;
use crate::error::GatewayStorageError;
use nym_credentials_interface::{AvailableBandwidth, ClientTicket, CredentialSpendingData};
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
use sqlx::FromRow;
@@ -24,24 +24,24 @@ pub struct PersistedSharedKeys {
}
impl TryFrom<PersistedSharedKeys> for SharedGatewayKey {
type Error = StorageError;
type Error = GatewayStorageError;
fn try_from(value: PersistedSharedKeys) -> Result<Self, Self::Error> {
match (
&value.derived_aes256_gcm_siv_key,
&value.derived_aes128_ctr_blake3_hmac_keys_bs58,
) {
(None, None) => Err(StorageError::MissingSharedKey {
(None, None) => Err(GatewayStorageError::MissingSharedKey {
id: value.client_id,
}),
(Some(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))
}
(None, Some(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))
}
}
@@ -91,12 +91,12 @@ pub struct UnverifiedTicketData {
}
impl TryFrom<UnverifiedTicketData> for ClientTicket {
type Error = StorageError;
type Error = GatewayStorageError;
fn try_from(value: UnverifiedTicketData) -> Result<Self, Self::Error> {
Ok(ClientTicket {
spending_data: CredentialSpendingData::try_from_bytes(&value.data).map_err(|_| {
StorageError::MalformedStoredTicketData {
GatewayStorageError::MalformedStoredTicketData {
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 {
type Error = crate::error::StorageError;
type Error = crate::error::GatewayStorageError;
fn try_from(value: WireguardPeer) -> Result<Self, Self::Error> {
Ok(Self {
+1 -1
View File
@@ -282,7 +282,7 @@ impl Client {
}
}
pub async fn create_delete_request<K, V>(
pub fn create_delete_request<K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
+113 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt;
use std::{fmt, str::FromStr};
use http::HeaderValue;
use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned};
@@ -15,6 +15,36 @@ pub struct UserAgent {
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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abbreviated_commit = self.git_commit.chars().take(7).collect::<String>();
@@ -55,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());
}
}
-1
View File
@@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
pub mod middleware;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum FormattedResponse<T> {
Json(Json<T>),
Yaml(Yaml<T>),
+2 -4
View File
@@ -26,7 +26,7 @@ url = { workspace = true }
time.workspace = true
thiserror = { workspace = true }
nym-crypto = { path = "../crypto" }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
nym-sphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" }
nym-sphinx-addressing = { path = "../nymsphinx/addressing" }
@@ -35,7 +35,5 @@ nym-sphinx-framing = { path = "../nymsphinx/framing" }
nym-sphinx-params = { path = "../nymsphinx/params" }
nym-sphinx-types = { path = "../nymsphinx/types" }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-bin-common = { path = "../bin-common" }
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>
// SPDX-License-Identifier: Apache-2.0
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())
}
}
-370
View File
@@ -1,370 +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_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;
// 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 {
/// 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 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 {
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,
}
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "nym-common-models"
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]
anyhow = { workspace = true }
bincode = { workspace = true }
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
serde = { workspace = true, features = ["derive"] }

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