Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efb2aed9dd | |||
| aaec2c9e82 | |||
| f6a6601495 | |||
| 66fea38d20 | |||
| c29fce0856 | |||
| 33bdf08804 | |||
| 236555e6c1 | |||
| c54760bb0b | |||
| 1b8a929ff5 | |||
| 72a4624ace | |||
| e5e7ddb0b6 | |||
| 5a07b73375 | |||
| d1f702c4aa | |||
| c20c7147f8 | |||
| 06956226ad | |||
| b06091e548 | |||
| f3bf5d080b | |||
| e06d442e95 | |||
| fc79f739d4 | |||
| 6ee8ccbeaa | |||
| cfebd14655 | |||
| 4851614375 | |||
| ec502f46f0 | |||
| 841fb81d24 | |||
| a9e62889c3 | |||
| 074d705448 | |||
| 4a9a5579c4 | |||
| 96180275f8 | |||
| ab20260a2f | |||
| ec1c564c2b | |||
| bdf97bcbd6 | |||
| 889d464e98 | |||
| 56206433e6 | |||
| 8e9d01c47b | |||
| f95f01959c | |||
| 42de620951 | |||
| af9f7b1c0f | |||
| 7c1ad7d20c | |||
| 9ac0595a35 | |||
| c6c138167d | |||
| 09633dead1 | |||
| cd2ad0adbb | |||
| 0b52224917 | |||
| 96ebe3fc4f | |||
| 8e713d43e1 | |||
| e7f806219c | |||
| 35aa7e338d | |||
| 2a60b2f057 | |||
| edd3f9108a | |||
| dcde4c8df1 | |||
| fcaa32284b | |||
| 3c56977fb5 | |||
| 5f3bb5db82 | |||
| 1b84639c34 | |||
| 546a486f9f | |||
| fa72f90bfa | |||
| 5668e123d9 | |||
| 27637ae6b4 | |||
| 12b9aefa99 | |||
| 0041937ed3 | |||
| 5cda49f996 | |||
| 36657bcd97 | |||
| 6167243a10 | |||
| 920276f2ac | |||
| 0e5bd966dd | |||
| 6acd936368 | |||
| 1f53da7456 | |||
| 2fdc0dc47b | |||
| a720c95dd7 | |||
| 01c7b2819e | |||
| 042a8a58aa | |||
| d8ab2a8f15 | |||
| 013941dbaf | |||
| 1af6e1ecdd | |||
| d6d2239685 | |||
| 3d704fbbf1 | |||
| 119c36b0bb | |||
| 6b5b97199b | |||
| d2e85f2bfe | |||
| b28e953a2b | |||
| b4ca959800 |
@@ -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
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
check-schema:
|
||||
name: Generate and check schema
|
||||
runs-on: arc-ubuntu-20.04
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: arc-ubuntu-20.04
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -4,6 +4,16 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.13-magura-drift] (2024-11-29)
|
||||
|
||||
- Optimised syncing bandwidth information to storage
|
||||
|
||||
## [2024.13-magura-patched] (2024-11-22)
|
||||
|
||||
- [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution
|
||||
- Introduced a configurable limit on retransmission frequency of packets if ACKs are not received
|
||||
- Filtered out invalid IP addresses on nym-api
|
||||
|
||||
## [2024.13-magura] (2024-11-18)
|
||||
|
||||
- Limit race probability ([#5145])
|
||||
|
||||
Generated
+173
-150
@@ -11,7 +11,7 @@ dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -283,7 +283,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -352,7 +352,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -363,7 +363,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -573,7 +573,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1140,7 +1140,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1790,7 +1790,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1948,7 +1948,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.11.1",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1970,7 +1970,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
||||
dependencies = [
|
||||
"darling_core 0.20.9",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2021,7 +2021,7 @@ dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2064,7 +2064,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2097,7 +2097,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2165,7 +2165,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2428,7 +2428,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.5.20",
|
||||
@@ -2531,7 +2531,7 @@ dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2765,7 +2765,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2860,7 +2860,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3248,6 +3248,12 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "human-repr"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
@@ -3822,9 +3828,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.162"
|
||||
version = "0.2.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -3950,7 +3956,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3961,7 +3967,7 @@ checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3974,7 +3980,7 @@ dependencies = [
|
||||
"macroific_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4128,6 +4134,21 @@ dependencies = [
|
||||
"wasm-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mixnet-connectivity-check"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.5.20",
|
||||
"futures",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"nym-sdk",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moka"
|
||||
version = "0.12.8"
|
||||
@@ -4431,7 +4452,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4451,7 +4472,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4470,6 +4491,7 @@ dependencies = [
|
||||
"cw2",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"dashmap",
|
||||
"dirs",
|
||||
"futures",
|
||||
"getset",
|
||||
@@ -4700,7 +4722,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.44"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -4751,12 +4773,14 @@ dependencies = [
|
||||
"nym-coconut-dkg-common",
|
||||
"nym-config",
|
||||
"nym-contracts-common",
|
||||
"nym-credential-proxy-requests",
|
||||
"nym-credential-storage",
|
||||
"nym-credential-utils",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-time",
|
||||
"nym-id",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
@@ -4781,7 +4805,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -4848,6 +4872,7 @@ dependencies = [
|
||||
"nym-gateway-requests",
|
||||
"nym-id",
|
||||
"nym-metrics",
|
||||
"nym-mixnet-client",
|
||||
"nym-network-defaults",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-pemstore",
|
||||
@@ -5006,16 +5031,6 @@ dependencies = [
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-common-models"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"nym-crypto",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-compact-ecash"
|
||||
version = "0.1.0"
|
||||
@@ -5450,7 +5465,7 @@ dependencies = [
|
||||
"nym-mixnode-common",
|
||||
"nym-network-defaults",
|
||||
"nym-network-requester",
|
||||
"nym-node-http-api",
|
||||
"nym-node-metrics",
|
||||
"nym-sdk",
|
||||
"nym-sphinx",
|
||||
"nym-statistics-common",
|
||||
@@ -5542,8 +5557,11 @@ name = "nym-gateway-stats-storage"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-credentials-interface",
|
||||
"nym-node-metrics",
|
||||
"nym-sphinx",
|
||||
"nym-statistics-common",
|
||||
"sqlx",
|
||||
"strum 0.26.3",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
@@ -5554,7 +5572,6 @@ dependencies = [
|
||||
name = "nym-gateway-storage"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bincode",
|
||||
"defguard_wireguard_rs",
|
||||
"log",
|
||||
@@ -5747,11 +5764,11 @@ name = "nym-mixnet-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"nym-sphinx",
|
||||
"nym-task",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5778,35 +5795,6 @@ dependencies = [
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.37"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"futures",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-http-api-common",
|
||||
"nym-metrics",
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnode-common",
|
||||
"nym-node-http-api",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-sphinx",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-types",
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"nym-types",
|
||||
"nym-validator-client",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode-common"
|
||||
version = "0.1.0"
|
||||
@@ -5815,11 +5803,9 @@ dependencies = [
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"nym-metrics",
|
||||
"nym-network-defaults",
|
||||
"nym-node-http-api",
|
||||
"nym-sphinx-acknowledgements",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-forwarding",
|
||||
@@ -5827,7 +5813,6 @@ dependencies = [
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-types",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"rand",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -5868,7 +5853,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-monitor"
|
||||
version = "0.1.0"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.7",
|
||||
@@ -5877,6 +5862,7 @@ dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-client-core",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"nym-sdk",
|
||||
@@ -5898,7 +5884,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.44"
|
||||
version = "1.1.46"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -5949,9 +5935,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.1.10"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"axum 0.7.7",
|
||||
"axum-extra",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"cargo_metadata 0.18.1",
|
||||
@@ -5959,6 +5948,10 @@ dependencies = [
|
||||
"clap 4.5.20",
|
||||
"colored",
|
||||
"cupid",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"headers",
|
||||
"human-repr",
|
||||
"humantime-serde",
|
||||
"ipnetwork 0.20.0",
|
||||
"nym-authenticator",
|
||||
@@ -5967,63 +5960,60 @@ dependencies = [
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
"nym-gateway",
|
||||
"nym-gateway-stats-storage",
|
||||
"nym-http-api-common",
|
||||
"nym-ip-packet-router",
|
||||
"nym-mixnode",
|
||||
"nym-metrics",
|
||||
"nym-mixnet-client",
|
||||
"nym-network-requester",
|
||||
"nym-node-http-api",
|
||||
"nym-node-metrics",
|
||||
"nym-node-requests",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-pemstore",
|
||||
"nym-sphinx-acknowledgements",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-forwarding",
|
||||
"nym-sphinx-framing",
|
||||
"nym-sphinx-types",
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"nym-types",
|
||||
"nym-validator-client",
|
||||
"nym-verloc",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-types",
|
||||
"rand",
|
||||
"semver 1.0.23",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"si-scale",
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"toml 0.8.14",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-http-api"
|
||||
name = "nym-node-metrics"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum 0.7.7",
|
||||
"axum-extra",
|
||||
"base64 0.22.1",
|
||||
"colored",
|
||||
"dashmap",
|
||||
"fastrand 2.1.1",
|
||||
"headers",
|
||||
"hmac",
|
||||
"hyper 1.4.1",
|
||||
"ipnetwork 0.20.0",
|
||||
"nym-crypto",
|
||||
"nym-http-api-common",
|
||||
"futures",
|
||||
"nym-metrics",
|
||||
"nym-node-requests",
|
||||
"nym-task",
|
||||
"nym-wireguard",
|
||||
"rand",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"nym-statistics-common",
|
||||
"strum 0.26.3",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower 0.4.13",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6055,30 +6045,23 @@ name = "nym-node-status-agent"
|
||||
version = "1.0.0-rc.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"clap 4.5.20",
|
||||
"nym-bin-common",
|
||||
"nym-common-models",
|
||||
"nym-crypto",
|
||||
"nym-node-status-client",
|
||||
"rand",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "1.0.0-rc.1"
|
||||
version = "1.0.0-rc.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.7",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"clap 4.5.20",
|
||||
"cosmwasm-std",
|
||||
@@ -6086,11 +6069,14 @@ dependencies = [
|
||||
"futures-util",
|
||||
"moka",
|
||||
"nym-bin-common",
|
||||
"nym-common-models",
|
||||
"nym-crypto",
|
||||
"nym-explorer-client",
|
||||
"nym-network-defaults",
|
||||
"nym-node-metrics",
|
||||
"nym-node-requests",
|
||||
"nym-node-status-client",
|
||||
"nym-serde-helpers",
|
||||
"nym-statistics-common",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"regex",
|
||||
@@ -6102,6 +6088,7 @@ dependencies = [
|
||||
"strum 0.26.3",
|
||||
"strum_macros 0.26.4",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-http",
|
||||
@@ -6113,6 +6100,21 @@ dependencies = [
|
||||
"utoipauto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-tester-utils"
|
||||
version = "0.1.0"
|
||||
@@ -6305,7 +6307,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -6807,6 +6809,25 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-verloc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
"humantime 2.1.0",
|
||||
"nym-crypto",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"rand",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.7.0"
|
||||
@@ -6885,8 +6906,10 @@ dependencies = [
|
||||
"nym-task",
|
||||
"nym-wireguard-types",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
@@ -6907,7 +6930,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7220,7 +7243,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7307,7 +7330,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7348,7 +7371,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7563,14 +7586,14 @@ dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -7583,7 +7606,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
@@ -7646,7 +7669,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7879,7 +7902,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8106,7 +8129,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rocket_http",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"unicode-xid",
|
||||
"version_check",
|
||||
]
|
||||
@@ -8232,7 +8255,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -8288,9 +8311,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.40"
|
||||
version = "0.38.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
|
||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"errno",
|
||||
@@ -8481,7 +8504,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals 0.29.1",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8513,7 +8536,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8645,7 +8668,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8656,7 +8679,7 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8667,7 +8690,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8729,7 +8752,7 @@ checksum = "aafbefbe175fa9bf03ca83ef89beecff7d2a95aaacd5732325b90ac8c3bd7b90"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8750,7 +8773,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8801,7 +8824,7 @@ dependencies = [
|
||||
"darling 0.20.9",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9400,7 +9423,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9443,9 +9466,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.87"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -9751,7 +9774,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9882,7 +9905,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10161,7 +10184,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10325,7 +10348,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@@ -10352,7 +10375,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals 0.28.0",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10555,7 +10578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10587,7 +10610,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"toml 0.5.11",
|
||||
"uniffi_build",
|
||||
"uniffi_meta",
|
||||
@@ -10719,7 +10742,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -10758,7 +10781,7 @@ checksum = "17e82ab96c5a55263b5bed151b8426410d93aa909a453acdbd4b6792b5af7d64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10769,7 +10792,7 @@ checksum = "86b8338dc3c9526011ffaa2aa6bd60ddfda9d49d2123108690755c6e34844212"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"utoipauto-core",
|
||||
]
|
||||
|
||||
@@ -10876,7 +10899,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -10910,7 +10933,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -10944,7 +10967,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11472,7 +11495,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11492,7 +11515,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+8
-11
@@ -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",
|
||||
@@ -105,7 +104,6 @@ members = [
|
||||
"explorer-api/explorer-client",
|
||||
"gateway",
|
||||
"integrations/bity",
|
||||
"mixnode",
|
||||
"sdk/ffi/cpp",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/shared",
|
||||
@@ -124,10 +122,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 +148,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 +260,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"
|
||||
|
||||
+68
-56
@@ -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,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -393,13 +393,20 @@ pub struct Traffic {
|
||||
/// poisson distribution.
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
pub deterministic_route_selection: bool,
|
||||
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
/// None - no limit
|
||||
pub maximum_number_of_retransmissions: Option<u32>,
|
||||
|
||||
/// Specifies the packet size used for sent messages.
|
||||
/// Do not override it unless you understand the consequences of that change.
|
||||
pub primary_packet_size: PacketSize,
|
||||
|
||||
/// Specifies the optional auxiliary packet size for optimizing message streams.
|
||||
/// Note that its use decreases overall anonymity.
|
||||
/// Do not set it it unless you understand the consequences of that change.
|
||||
/// Do not set it unless you understand the consequences of that change.
|
||||
pub secondary_packet_size: Option<PacketSize>,
|
||||
|
||||
pub packet_type: PacketType,
|
||||
@@ -424,6 +431,8 @@ impl Default for Traffic {
|
||||
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||
disable_main_poisson_packet_distribution: false,
|
||||
deterministic_route_selection: false,
|
||||
maximum_number_of_retransmissions: None,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
secondary_packet_size: None,
|
||||
packet_type: PacketType::Mix,
|
||||
|
||||
@@ -111,6 +111,7 @@ impl From<ConfigV5> for Config {
|
||||
primary_packet_size: value.debug.traffic.primary_packet_size,
|
||||
secondary_packet_size: value.debug.traffic.secondary_packet_size,
|
||||
packet_type: value.debug.traffic.packet_type,
|
||||
..Default::default()
|
||||
},
|
||||
cover_traffic: CoverTraffic {
|
||||
loop_cover_traffic_average_delay: value
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-9
@@ -30,7 +30,8 @@ pub(crate) enum Action {
|
||||
InsertPending(Vec<PendingAcknowledgement>),
|
||||
|
||||
/// Removes given `PendingAcknowledgement` from the 'shared' state. Also cancels the retransmission timer.
|
||||
/// Initiated by `AcknowledgementListener`
|
||||
/// Initiated by `AcknowledgementListener` upon receiving the acknowledgement. Also by `RetransmissionRequestListener`
|
||||
/// upon deciding to abandon the data.
|
||||
RemovePending(FragmentIdentifier),
|
||||
|
||||
/// Starts the retransmission timer on given `PendingAcknowledgement` with the `Duration` based on
|
||||
@@ -41,7 +42,7 @@ pub(crate) enum Action {
|
||||
|
||||
/// Updates the expected delay of given `PendingAcknowledgement` with the new provided `SphinxDelay`.
|
||||
/// Initiated by `RetransmissionRequestListener`
|
||||
UpdateDelay(FragmentIdentifier, SphinxDelay),
|
||||
UpdatePendingAck(FragmentIdentifier, SphinxDelay),
|
||||
}
|
||||
|
||||
impl Action {
|
||||
@@ -57,8 +58,8 @@ impl Action {
|
||||
Action::StartTimer(frag_id)
|
||||
}
|
||||
|
||||
pub(crate) fn new_update_delay(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
|
||||
Action::UpdateDelay(frag_id, delay)
|
||||
pub(crate) fn new_update_pending_ack(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
|
||||
Action::UpdatePendingAck(frag_id, delay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ impl ActionController {
|
||||
}
|
||||
|
||||
fn handle_start_timer(&mut self, frag_id: FragmentIdentifier) {
|
||||
trace!("{} is starting its timer", frag_id);
|
||||
trace!("{frag_id} is starting its timer");
|
||||
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
|
||||
// the fact that this branch is now POSSIBLE is a sign of a need to refactor this whole
|
||||
@@ -193,7 +194,7 @@ impl ActionController {
|
||||
|
||||
// initiated basically as a first step of retransmission. At first data has its delay updated
|
||||
// (as new sphinx packet was created with new expected delivery time)
|
||||
fn handle_update_delay(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
|
||||
fn handle_update_pending_ack(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
|
||||
trace!("{} is updating its delay", frag_id);
|
||||
// TODO: is it possible to solve this without either locking or temporarily removing the value?
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
|
||||
@@ -202,7 +203,7 @@ impl ActionController {
|
||||
// reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference
|
||||
// was dropped hence this unwrap is safe.
|
||||
let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap();
|
||||
inner_data.update_delay(delay);
|
||||
inner_data.update_retransmitted(delay);
|
||||
|
||||
self.pending_acks_data
|
||||
.insert(frag_id, (Arc::new(inner_data), queue_key));
|
||||
@@ -225,7 +226,7 @@ impl ActionController {
|
||||
// about it. Perhaps just reschedule it at later point?
|
||||
let frag_id = expired_ack.into_inner();
|
||||
|
||||
trace!("{} has expired", frag_id);
|
||||
trace!("{frag_id} has expired");
|
||||
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
|
||||
if queue_key.is_none() {
|
||||
@@ -258,7 +259,9 @@ impl ActionController {
|
||||
Action::InsertPending(pending_acks) => self.handle_insert(pending_acks),
|
||||
Action::RemovePending(frag_id) => self.handle_remove(frag_id),
|
||||
Action::StartTimer(frag_id) => self.handle_start_timer(frag_id),
|
||||
Action::UpdateDelay(frag_id, delay) => self.handle_update_delay(frag_id, delay),
|
||||
Action::UpdatePendingAck(frag_id, delay) => {
|
||||
self.handle_update_pending_ack(frag_id, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ pub(crate) struct PendingAcknowledgement {
|
||||
delay: SphinxDelay,
|
||||
destination: PacketDestination,
|
||||
mix_hops: Option<u8>,
|
||||
retransmissions: u32,
|
||||
}
|
||||
|
||||
impl PendingAcknowledgement {
|
||||
@@ -86,6 +87,7 @@ impl PendingAcknowledgement {
|
||||
delay,
|
||||
destination: PacketDestination::KnownRecipient(recipient.into()),
|
||||
mix_hops,
|
||||
retransmissions: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +107,7 @@ impl PendingAcknowledgement {
|
||||
// Messages sent using SURBs are using the number of mix hops set by the recipient when
|
||||
// they provided the SURBs, so it doesn't make sense to include it here.
|
||||
mix_hops: None,
|
||||
retransmissions: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +119,9 @@ impl PendingAcknowledgement {
|
||||
self.message_chunk.clone()
|
||||
}
|
||||
|
||||
fn update_delay(&mut self, new_delay: SphinxDelay) {
|
||||
fn update_retransmitted(&mut self, new_delay: SphinxDelay) {
|
||||
self.delay = new_delay;
|
||||
self.retransmissions += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +167,9 @@ impl AcknowledgementControllerConnectors {
|
||||
|
||||
/// Configurable parameters of the `AcknowledgementController`
|
||||
pub(super) struct Config {
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
maximum_retransmissions: Option<u32>,
|
||||
|
||||
/// Given ack timeout in the form a * BASE_DELAY + b, it specifies the additive part `b`
|
||||
ack_wait_addition: Duration,
|
||||
|
||||
@@ -174,8 +181,13 @@ pub(super) struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub(super) fn new(ack_wait_addition: Duration, ack_wait_multiplier: f64) -> Self {
|
||||
pub(super) fn new(
|
||||
maximum_retransmissions: Option<u32>,
|
||||
ack_wait_addition: Duration,
|
||||
ack_wait_multiplier: f64,
|
||||
) -> Self {
|
||||
Config {
|
||||
maximum_retransmissions,
|
||||
ack_wait_addition,
|
||||
ack_wait_multiplier,
|
||||
packet_size: Default::default(),
|
||||
@@ -238,6 +250,7 @@ where
|
||||
|
||||
// will listen for any ack timeouts and trigger retransmission
|
||||
let retransmission_request_listener = RetransmissionRequestListener::new(
|
||||
config.maximum_retransmissions,
|
||||
connectors.ack_action_sender.clone(),
|
||||
message_handler,
|
||||
retransmission_rx,
|
||||
|
||||
+16
-3
@@ -20,6 +20,7 @@ use std::sync::{Arc, Weak};
|
||||
|
||||
// responsible for packet retransmission upon fired timer
|
||||
pub(super) struct RetransmissionRequestListener<R> {
|
||||
maximum_retransmissions: Option<u32>,
|
||||
action_sender: AckActionSender,
|
||||
message_handler: MessageHandler<R>,
|
||||
request_receiver: RetransmissionRequestReceiver,
|
||||
@@ -31,12 +32,14 @@ where
|
||||
R: CryptoRng + Rng,
|
||||
{
|
||||
pub(super) fn new(
|
||||
maximum_retransmissions: Option<u32>,
|
||||
action_sender: AckActionSender,
|
||||
message_handler: MessageHandler<R>,
|
||||
request_receiver: RetransmissionRequestReceiver,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
) -> Self {
|
||||
RetransmissionRequestListener {
|
||||
maximum_retransmissions,
|
||||
action_sender,
|
||||
message_handler,
|
||||
request_receiver,
|
||||
@@ -77,6 +80,18 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
|
||||
|
||||
if let Some(limit) = self.maximum_retransmissions {
|
||||
if timed_out_ack.retransmissions >= limit {
|
||||
warn!("reached maximum number of allowed retransmissions for the packet");
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_remove(frag_id))
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_prepared_fragment = match &timed_out_ack.destination {
|
||||
PacketDestination::Anonymous {
|
||||
recipient_tag,
|
||||
@@ -101,8 +116,6 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
|
||||
|
||||
let prepared_fragment = match maybe_prepared_fragment {
|
||||
Ok(prepared_fragment) => prepared_fragment,
|
||||
Err(err) => {
|
||||
@@ -136,7 +149,7 @@ where
|
||||
// with the additional poisson delay.
|
||||
// And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer`
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_update_delay(frag_id, new_delay))
|
||||
.unbounded_send(Action::new_update_pending_ack(frag_id, new_delay))
|
||||
.unwrap();
|
||||
|
||||
// send to `OutQueueControl` to eventually send to the mix network
|
||||
|
||||
@@ -91,6 +91,9 @@ pub(crate) struct Config {
|
||||
/// and surb-based are going to be sent.
|
||||
sender_address: Recipient,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
deterministic_route_selection: bool,
|
||||
|
||||
/// Average delay a data packet is going to get delay at a single mixnode.
|
||||
average_packet_delay: Duration,
|
||||
|
||||
@@ -114,10 +117,12 @@ impl Config {
|
||||
sender_address: Recipient,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
deterministic_route_selection: bool,
|
||||
) -> Self {
|
||||
Config {
|
||||
ack_key,
|
||||
sender_address,
|
||||
deterministic_route_selection,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
||||
@@ -176,6 +181,7 @@ where
|
||||
{
|
||||
let message_preparer = MessagePreparer::new(
|
||||
rng,
|
||||
config.deterministic_route_selection,
|
||||
config.sender_address,
|
||||
config.average_packet_delay,
|
||||
config.average_ack_delay,
|
||||
@@ -634,7 +640,7 @@ where
|
||||
|
||||
pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) {
|
||||
self.action_sender
|
||||
.unbounded_send(Action::UpdateDelay(id, new_delay))
|
||||
.unbounded_send(Action::UpdatePendingAck(id, new_delay))
|
||||
.expect("action control task has died")
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ pub struct Config {
|
||||
impl<'a> From<&'a Config> for acknowledgement_control::Config {
|
||||
fn from(cfg: &'a Config) -> Self {
|
||||
acknowledgement_control::Config::new(
|
||||
cfg.traffic.maximum_number_of_retransmissions,
|
||||
cfg.acks.ack_wait_addition,
|
||||
cfg.acks.ack_wait_multiplier,
|
||||
)
|
||||
@@ -97,6 +98,7 @@ impl<'a> From<&'a Config> for message_handler::Config {
|
||||
cfg.self_recipient,
|
||||
cfg.traffic.average_packet_delay,
|
||||
cfg.acks.average_ack_delay,
|
||||
cfg.traffic.deterministic_route_selection,
|
||||
)
|
||||
.with_custom_primary_packet_size(cfg.traffic.primary_packet_size)
|
||||
.with_custom_secondary_packet_size(cfg.traffic.secondary_packet_size)
|
||||
|
||||
@@ -38,7 +38,7 @@ pub struct TopologyReadPermit<'a> {
|
||||
permit: RwLockReadGuard<'a, Option<NymTopology>>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for TopologyReadPermit<'a> {
|
||||
impl Deref for TopologyReadPermit<'_> {
|
||||
type Target = Option<NymTopology>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
||||
@@ -46,7 +46,8 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
|
||||
#[cfg(unix)]
|
||||
match _conn.get_ref() {
|
||||
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
|
||||
&_ => None,
|
||||
MaybeTlsStream::Rustls(tls_stream) => Some(tls_stream.as_raw_fd()),
|
||||
_ => None,
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
None
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -32,7 +32,7 @@ impl Div<GasPrice> for Coin {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Div<GasPrice> for &'a Coin {
|
||||
impl Div<GasPrice> for &Coin {
|
||||
type Output = Gas;
|
||||
|
||||
fn div(self, rhs: GasPrice) -> Self::Output {
|
||||
|
||||
@@ -22,7 +22,7 @@ pub struct GasPrice {
|
||||
pub denom: String,
|
||||
}
|
||||
|
||||
impl<'a> Mul<Gas> for &'a GasPrice {
|
||||
impl Mul<Gas> for &GasPrice {
|
||||
type Output = Coin;
|
||||
|
||||
fn mul(self, gas_limit: Gas) -> Self::Output {
|
||||
|
||||
@@ -98,6 +98,13 @@ impl DirectSecp256k1HdWallet {
|
||||
Ok((private_key, public_key))
|
||||
}
|
||||
|
||||
pub fn derive_extended_private_key(
|
||||
&self,
|
||||
hd_path: &DerivationPath,
|
||||
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
|
||||
Ok(XPrv::derive_from_path(self.seed, hd_path)?)
|
||||
}
|
||||
|
||||
pub fn try_derive_accounts(&self) -> Result<Vec<AccountData>, DirectSecp256k1HdWalletError> {
|
||||
let mut accounts = Vec::with_capacity(self.accounts.len());
|
||||
for derivation_info in &self.accounts {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
@@ -3,5 +3,6 @@
|
||||
|
||||
pub mod context;
|
||||
pub mod ecash;
|
||||
pub mod internal;
|
||||
pub mod utils;
|
||||
pub mod validator;
|
||||
|
||||
@@ -32,7 +32,7 @@ pub(crate) mod string_rfc3339_offset_date_time {
|
||||
|
||||
struct Rfc3339OffsetDateTimeVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for Rfc3339OffsetDateTimeVisitor {
|
||||
impl Visitor<'_> for Rfc3339OffsetDateTimeVisitor {
|
||||
type Value = OffsetDateTime;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::ClientBandwidth;
|
||||
use nym_credentials::ecash::utils::ecash_today;
|
||||
use nym_credentials_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,
|
||||
@@ -111,7 +111,7 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
async fn sync_storage_bandwidth(&mut self) -> Result<()> {
|
||||
pub async fn sync_storage_bandwidth(&mut self) -> Result<()> {
|
||||
trace!("syncing client bandwidth with the underlying storage");
|
||||
let updated = self
|
||||
.storage
|
||||
|
||||
@@ -8,8 +8,8 @@ use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5);
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_secs(5 * 60); // 5 minutes
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BandwidthFlushingBehaviourConfig {
|
||||
|
||||
@@ -13,7 +13,6 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
||||
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
|
||||
use nym_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();
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -221,7 +221,17 @@ impl From<PayInfo> for NymPayInfo {
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Serialize, Deserialize, strum::Display, strum::EnumString,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Hash,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
strum::EnumIter,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
|
||||
@@ -41,6 +41,6 @@ aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"]
|
||||
serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"]
|
||||
asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"]
|
||||
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"]
|
||||
stream_cipher = ["aes", "ctr", "cipher", "generic-array"]
|
||||
stream_cipher = ["aes", "ctr", "cipher", "cipher/zeroize", "generic-array"]
|
||||
sphinx = ["nym-sphinx-types/sphinx"]
|
||||
outfox = ["nym-sphinx-types/outfox"]
|
||||
|
||||
@@ -26,9 +26,8 @@ const PARALLEL_RUNS: usize = 32;
|
||||
/// `lambda` ($\lambda$) in the DKG paper
|
||||
const SECURITY_PARAMETER: usize = 256;
|
||||
|
||||
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
|
||||
/// ceil(SECURITY_PARAMETER / PARALLEL_RUNS) in the paper
|
||||
const NUM_CHALLENGE_BITS: usize = (SECURITY_PARAMETER + PARALLEL_RUNS - 1) / PARALLEL_RUNS;
|
||||
const NUM_CHALLENGE_BITS: usize = SECURITY_PARAMETER.div_ceil(PARALLEL_RUNS);
|
||||
|
||||
// type alias for ease of use
|
||||
type FirstChallenge = Vec<Vec<Vec<u64>>>;
|
||||
|
||||
@@ -196,7 +196,7 @@ impl<'b> Add<&'b Polynomial> for Polynomial {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<Polynomial> for &'a Polynomial {
|
||||
impl Add<Polynomial> for &Polynomial {
|
||||
type Output = Polynomial;
|
||||
|
||||
fn add(self, rhs: Polynomial) -> Polynomial {
|
||||
@@ -212,10 +212,10 @@ impl Add<Polynomial> for Polynomial {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Polynomial> for &'a Polynomial {
|
||||
impl<'a> Add<&'a Polynomial> for &Polynomial {
|
||||
type Output = Polynomial;
|
||||
|
||||
fn add(self, rhs: &'b Polynomial) -> Self::Output {
|
||||
fn add(self, rhs: &'a Polynomial) -> Self::Output {
|
||||
let len = self.coefficients.len();
|
||||
let rhs_len = rhs.coefficients.len();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ pub struct GatewayHandshake<'a> {
|
||||
handshake_future: BoxFuture<'a, Result<SharedGatewayKey, HandshakeError>>,
|
||||
}
|
||||
|
||||
impl<'a> Future for GatewayHandshake<'a> {
|
||||
impl Future for GatewayHandshake<'_> {
|
||||
type Output = Result<SharedGatewayKey, HandshakeError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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?)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -282,6 +282,48 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_delete_request<K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> RequestBuilder
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = sanitize_url(&self.base_url, path, params);
|
||||
self.reqwest_client.delete(url)
|
||||
}
|
||||
|
||||
pub async fn send_delete_request<K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<Response, HttpClientError<E>>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
E: Display,
|
||||
{
|
||||
tracing::trace!("Sending DELETE request");
|
||||
let url = sanitize_url(&self.base_url, path, params);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
Ok(wasmtimer::tokio::timeout(
|
||||
self.request_timeout,
|
||||
self.reqwest_client.delete(url).send(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
Ok(self.reqwest_client.delete(url).send().await?)
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn get_json<T, K, V, E>(
|
||||
&self,
|
||||
@@ -315,6 +357,21 @@ impl Client {
|
||||
parse_response(res, true).await
|
||||
}
|
||||
|
||||
pub async fn delete_json<T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
E: Display + DeserializeOwned,
|
||||
{
|
||||
let res = self.send_delete_request(path, params).await?;
|
||||
parse_response(res, false).await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
@@ -380,6 +437,35 @@ impl Client {
|
||||
|
||||
parse_response(res, true).await
|
||||
}
|
||||
|
||||
pub async fn delete_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let res = {
|
||||
wasmtimer::tokio::timeout(
|
||||
self.request_timeout,
|
||||
self.reqwest_client
|
||||
.delete(self.base_url.join(endpoint.as_ref())?)
|
||||
.send(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let res = {
|
||||
self.reqwest_client
|
||||
.delete(self.base_url.join(endpoint.as_ref())?)
|
||||
.send()
|
||||
.await?
|
||||
};
|
||||
|
||||
parse_response(res, false).await
|
||||
}
|
||||
}
|
||||
|
||||
// define those methods on the trait for nicer extensions (and not having to type the thing twice)
|
||||
@@ -412,6 +498,17 @@ pub trait ApiClient {
|
||||
V: AsRef<str> + Sync,
|
||||
E: Display + DeserializeOwned;
|
||||
|
||||
async fn delete_json<T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str> + Sync,
|
||||
V: AsRef<str> + Sync,
|
||||
E: Display + DeserializeOwned;
|
||||
|
||||
/// `get` json data from the provided absolute endpoint, i.e. for example `"/api/v1/mixnodes?since=12345"`
|
||||
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
@@ -429,6 +526,12 @@ pub trait ApiClient {
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str> + Sync + Send;
|
||||
|
||||
async fn delete_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str> + Sync + Send;
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -464,6 +567,20 @@ impl ApiClient for Client {
|
||||
self.post_json(path, params, json_body).await
|
||||
}
|
||||
|
||||
async fn delete_json<T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str> + Sync,
|
||||
V: AsRef<str> + Sync,
|
||||
E: Display + DeserializeOwned,
|
||||
{
|
||||
self.delete_json(path, params).await
|
||||
}
|
||||
|
||||
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
@@ -486,6 +603,15 @@ impl ApiClient for Client {
|
||||
{
|
||||
self.post_json_endpoint(endpoint, json_body).await
|
||||
}
|
||||
|
||||
async fn delete_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str> + Sync + Send,
|
||||
{
|
||||
self.delete_json_endpoint(endpoint).await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in API urls forever.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// 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};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UserAgent {
|
||||
pub application: String,
|
||||
pub version: String,
|
||||
@@ -14,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>();
|
||||
@@ -54,3 +85,85 @@ impl From<BinaryBuildInformationOwned> for UserAgent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parsing_valid_user_agent() {
|
||||
let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg";
|
||||
let parsed = UserAgent::from_str(user_agent).unwrap();
|
||||
assert_eq!(
|
||||
parsed,
|
||||
UserAgent {
|
||||
application: "nym-mixnode".to_string(),
|
||||
version: "0.11.0".to_string(),
|
||||
platform: "x86_64-unknown-linux-gnu".to_string(),
|
||||
git_commit: "abcdefg".to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parsing_invalid_user_agent() {
|
||||
let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu";
|
||||
assert!(UserAgent::from_str(user_agent).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converting_user_agent_to_string() {
|
||||
let user_agent = UserAgent {
|
||||
application: "nym-mixnode".to_string(),
|
||||
version: "0.11.0".to_string(),
|
||||
platform: "x86_64-unknown-linux-gnu".to_string(),
|
||||
git_commit: "abcdefg".to_string(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
user_agent.to_string(),
|
||||
"nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converting_user_agent_to_display() {
|
||||
let user_agent = UserAgent {
|
||||
application: "nym-mixnode".to_string(),
|
||||
version: "0.11.0".to_string(),
|
||||
platform: "x86_64-unknown-linux-gnu".to_string(),
|
||||
git_commit: "abcdefg".to_string(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", user_agent),
|
||||
"nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converting_user_agent_to_header_value_fails() {
|
||||
let user_agent = UserAgent {
|
||||
application: "nym-mixnode".to_string(),
|
||||
version: "0.11.0".to_string(),
|
||||
platform: "x86_64-unknown-linux-gnu".to_string(),
|
||||
git_commit: "abcdefg".to_string(),
|
||||
};
|
||||
|
||||
let header_value: Result<HeaderValue, _> = user_agent.clone().try_into();
|
||||
assert!(header_value.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converting_user_agent_to_header_value_has_same_string_representation() {
|
||||
let user_agent = UserAgent {
|
||||
application: "nym-mixnode".to_string(),
|
||||
version: "0.11.0".to_string(),
|
||||
platform: "x86_64-unknown-linux-gnu".to_string(),
|
||||
git_commit: "abcdefg".to_string(),
|
||||
};
|
||||
|
||||
let header_value: HeaderValue = user_agent.clone().try_into().unwrap();
|
||||
assert_eq!(header_value.to_str().unwrap(), user_agent.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod packet_processor;
|
||||
pub mod verloc;
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -1,383 +0,0 @@
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::verloc::listener::PacketListener;
|
||||
use crate::verloc::sender::{PacketSender, TestedNode};
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::{self, parse_version};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_network_defaults::mainnet::NYM_API;
|
||||
use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult};
|
||||
use nym_task::TaskClient;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::sleep;
|
||||
use url::Url;
|
||||
|
||||
use measurement::VerlocStatsUpdateExt;
|
||||
|
||||
// pub use crate::verloc::measurement::{AtomicVerlocResult, Verloc, VerlocResult};
|
||||
|
||||
pub mod error;
|
||||
pub(crate) mod listener;
|
||||
pub(crate) mod measurement;
|
||||
pub(crate) mod packet;
|
||||
pub(crate) mod sender;
|
||||
|
||||
// TODO: MUST BE UPDATED BEFORE ACTUAL RELEASE!!
|
||||
pub const MINIMUM_NODE_VERSION: &str = "0.10.1";
|
||||
|
||||
// by default all of those are overwritten by config data from mixnodes directly
|
||||
const DEFAULT_VERLOC_PORT: u16 = 1790;
|
||||
const DEFAULT_PACKETS_PER_NODE: usize = 100;
|
||||
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
|
||||
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
|
||||
const DEFAULT_BATCH_SIZE: usize = 50;
|
||||
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
|
||||
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
/// Minimum semver version of a node (gateway or mixnode) that is capable of replying to echo packets.
|
||||
minimum_compatible_node_version: version_checker::Version,
|
||||
|
||||
/// Socket address of this node on which it will be listening for the measurement packets.
|
||||
listening_address: SocketAddr,
|
||||
|
||||
/// Specifies number of echo packets sent to each node during a measurement run.
|
||||
packets_per_node: usize,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
|
||||
packet_timeout: Duration,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the connection to get established.
|
||||
connection_timeout: Duration,
|
||||
|
||||
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
|
||||
delay_between_packets: Duration,
|
||||
|
||||
/// Specifies number of nodes being tested at once.
|
||||
tested_nodes_batch_size: usize,
|
||||
|
||||
/// Specifies delay between subsequent test runs.
|
||||
testing_interval: Duration,
|
||||
|
||||
/// Specifies delay between attempting to run the measurement again if the previous run failed
|
||||
/// due to being unable to get the list of nodes.
|
||||
retry_timeout: Duration,
|
||||
|
||||
/// URLs to the nym apis for obtaining network topology.
|
||||
nym_api_urls: Vec<Url>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn build() -> ConfigBuilder {
|
||||
ConfigBuilder::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct ConfigBuilder(Config);
|
||||
|
||||
impl ConfigBuilder {
|
||||
pub fn new() -> ConfigBuilder {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn minimum_compatible_node_version(mut self, version: version_checker::Version) -> Self {
|
||||
self.0.minimum_compatible_node_version = version;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn listening_address(mut self, listening_address: SocketAddr) -> Self {
|
||||
self.0.listening_address = listening_address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn packets_per_node(mut self, packets_per_node: usize) -> Self {
|
||||
self.0.packets_per_node = packets_per_node;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn packet_timeout(mut self, packet_timeout: Duration) -> Self {
|
||||
self.0.packet_timeout = packet_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn connection_timeout(mut self, connection_timeout: Duration) -> Self {
|
||||
self.0.connection_timeout = connection_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn delay_between_packets(mut self, delay_between_packets: Duration) -> Self {
|
||||
self.0.delay_between_packets = delay_between_packets;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tested_nodes_batch_size(mut self, tested_nodes_batch_size: usize) -> Self {
|
||||
self.0.tested_nodes_batch_size = tested_nodes_batch_size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn testing_interval(mut self, testing_interval: Duration) -> Self {
|
||||
self.0.testing_interval = testing_interval;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn retry_timeout(mut self, retry_timeout: Duration) -> Self {
|
||||
self.0.retry_timeout = retry_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn nym_api_urls(mut self, nym_api_urls: Vec<Url>) -> Self {
|
||||
self.0.nym_api_urls = nym_api_urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Config {
|
||||
// panics here are fine as those are only ever constructed at the initial setup
|
||||
assert!(
|
||||
!self.0.nym_api_urls.is_empty(),
|
||||
"at least one validator endpoint must be provided",
|
||||
);
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ConfigBuilder {
|
||||
fn default() -> Self {
|
||||
ConfigBuilder(Config {
|
||||
minimum_compatible_node_version: parse_version(MINIMUM_NODE_VERSION).unwrap(),
|
||||
listening_address: format!("[::]:{DEFAULT_VERLOC_PORT}").parse().unwrap(),
|
||||
packets_per_node: DEFAULT_PACKETS_PER_NODE,
|
||||
packet_timeout: DEFAULT_PACKET_TIMEOUT,
|
||||
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
|
||||
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
|
||||
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
|
||||
testing_interval: DEFAULT_TESTING_INTERVAL,
|
||||
retry_timeout: DEFAULT_RETRY_TIMEOUT,
|
||||
nym_api_urls: vec![NYM_API.parse().expect("Invalid default API URL")],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VerlocMeasurer {
|
||||
config: Config,
|
||||
packet_sender: Arc<PacketSender>,
|
||||
packet_listener: Arc<PacketListener>,
|
||||
shutdown_listener: TaskClient,
|
||||
|
||||
currently_used_api: usize,
|
||||
|
||||
// Note: this client is only fine here as it does not maintain constant connection to the validator.
|
||||
// It only does bunch of REST queries. If we update it at some point to a more sophisticated (maybe signing) client,
|
||||
// then it definitely cannot be constructed here and probably will need to be passed from outside,
|
||||
// as mixnodes/gateways would already be using an instance of said client.
|
||||
validator_client: nym_validator_client::NymApiClient,
|
||||
state: SharedVerlocStats,
|
||||
}
|
||||
|
||||
impl VerlocMeasurer {
|
||||
pub fn new(
|
||||
mut config: Config,
|
||||
identity: Arc<identity::KeyPair>,
|
||||
shutdown_listener: TaskClient,
|
||||
) -> Self {
|
||||
config.nym_api_urls.shuffle(&mut thread_rng());
|
||||
|
||||
VerlocMeasurer {
|
||||
packet_sender: Arc::new(PacketSender::new(
|
||||
Arc::clone(&identity),
|
||||
config.packets_per_node,
|
||||
config.packet_timeout,
|
||||
config.connection_timeout,
|
||||
config.delay_between_packets,
|
||||
shutdown_listener.clone().named("VerlocPacketSender"),
|
||||
)),
|
||||
packet_listener: Arc::new(PacketListener::new(
|
||||
config.listening_address,
|
||||
Arc::clone(&identity),
|
||||
shutdown_listener.clone().named("VerlocPacketListener"),
|
||||
)),
|
||||
shutdown_listener,
|
||||
currently_used_api: 0,
|
||||
validator_client: nym_validator_client::NymApiClient::new(
|
||||
config.nym_api_urls[0].clone(),
|
||||
),
|
||||
config,
|
||||
state: SharedVerlocStats::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_shared_state(&mut self, state: SharedVerlocStats) {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
fn use_next_nym_api(&mut self) {
|
||||
if self.config.nym_api_urls.len() == 1 {
|
||||
warn!("There's only a single validator API available - it won't be possible to use a different one");
|
||||
return;
|
||||
}
|
||||
|
||||
self.currently_used_api = (self.currently_used_api + 1) % self.config.nym_api_urls.len();
|
||||
self.validator_client
|
||||
.change_nym_api(self.config.nym_api_urls[self.currently_used_api].clone())
|
||||
}
|
||||
|
||||
fn start_listening(&self) -> JoinHandle<()> {
|
||||
let packet_listener = Arc::clone(&self.packet_listener);
|
||||
tokio::spawn(packet_listener.run())
|
||||
}
|
||||
|
||||
async fn perform_measurement(&self, nodes_to_test: Vec<TestedNode>) -> MeasurementOutcome {
|
||||
log::trace!("Performing measurements");
|
||||
if nodes_to_test.is_empty() {
|
||||
log::debug!("there are no nodes to measure");
|
||||
return MeasurementOutcome::Done;
|
||||
}
|
||||
|
||||
let mut shutdown_listener = self.shutdown_listener.clone().named("VerlocMeasurement");
|
||||
shutdown_listener.disarm();
|
||||
|
||||
for chunk in nodes_to_test.chunks(self.config.tested_nodes_batch_size) {
|
||||
let mut chunk_results = Vec::with_capacity(chunk.len());
|
||||
|
||||
let mut measurement_chunk = chunk
|
||||
.iter()
|
||||
.map(|node| {
|
||||
let node = *node;
|
||||
let packet_sender = Arc::clone(&self.packet_sender);
|
||||
// TODO: there's a potential issue here. if we make the measurement go into separate
|
||||
// task, we risk biasing results with the bunch of context switches overhead
|
||||
// but if we don't do it, it will take ages to complete
|
||||
|
||||
// TODO: check performance difference when it's not spawned as a separate task
|
||||
tokio::spawn(async move {
|
||||
(
|
||||
packet_sender.send_packets_to_node(node).await,
|
||||
node.identity,
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>();
|
||||
|
||||
// exhaust the results
|
||||
while !shutdown_listener.is_shutdown() {
|
||||
tokio::select! {
|
||||
measurement_result = measurement_chunk.next() => {
|
||||
let Some(result) = measurement_result else {
|
||||
// if the stream has finished, it means we got everything we could have gotten
|
||||
break
|
||||
};
|
||||
|
||||
// if we receive JoinError it means the task failed to get executed, so either there's a bigger issue with tokio
|
||||
// or there was a panic inside the task itself. In either case, we should just terminate ourselves.
|
||||
let execution_result = result.expect("the measurement task panicked!");
|
||||
let identity = execution_result.1;
|
||||
|
||||
let measurement_result = match execution_result.0 {
|
||||
Err(err) => {
|
||||
debug!("Failed to perform measurement for {identity}: {err}");
|
||||
None
|
||||
}
|
||||
Ok(result) => Some(result),
|
||||
};
|
||||
chunk_results.push(VerlocNodeResult::new(identity, measurement_result));
|
||||
},
|
||||
_ = shutdown_listener.recv() => {
|
||||
trace!("Shutdown received while measuring");
|
||||
return MeasurementOutcome::Shutdown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the results vector with chunks as they become available (by default every 50 nodes)
|
||||
self.state.append_measurement_results(chunk_results).await;
|
||||
}
|
||||
|
||||
MeasurementOutcome::Done
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
self.start_listening();
|
||||
|
||||
while !self.shutdown_listener.is_shutdown() {
|
||||
info!("Starting verloc measurements");
|
||||
// TODO: should we also measure gateways?
|
||||
|
||||
let all_mixes = match self.validator_client.get_all_described_nodes().await {
|
||||
Ok(nodes) => nodes,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to obtain list of mixnodes from the validator - {}. Going to attempt to use another validator API in the next run",
|
||||
err
|
||||
);
|
||||
self.use_next_nym_api();
|
||||
sleep(self.config.retry_timeout).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if all_mixes.is_empty() {
|
||||
warn!("There does not seem there are any nodes to measure...")
|
||||
}
|
||||
|
||||
// we only care about address and identity
|
||||
let tested_nodes = all_mixes
|
||||
.into_iter()
|
||||
.filter(|n| n.description.declared_role.mixnode)
|
||||
.filter_map(|node| {
|
||||
// try to parse the identity and host
|
||||
let node_identity = node.ed25519_identity_key();
|
||||
|
||||
let ip = node.description.host_information.ip_address.first()?;
|
||||
let verloc_port = node.description.verloc_port();
|
||||
let verloc_host = SocketAddr::new(*ip, verloc_port);
|
||||
|
||||
// TODO: possible problem in the future, this does name resolution and theoretically
|
||||
// if a lot of nodes maliciously mis-configured themselves, it might take a while to resolve them all
|
||||
// However, maybe it's not a problem as if they are misconfigured, they will eventually be
|
||||
// pushed out of the network and on top of that, verloc is done in separate task that runs
|
||||
// only every few hours.
|
||||
Some(TestedNode::new(verloc_host, node_identity))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// on start of each run remove old results
|
||||
self.state.start_new_measurements(tested_nodes.len()).await;
|
||||
|
||||
if let MeasurementOutcome::Shutdown = self.perform_measurement(tested_nodes).await {
|
||||
log::trace!("Shutting down after aborting measurements");
|
||||
break;
|
||||
}
|
||||
|
||||
// write current time to "run finished" field
|
||||
self.state.finish_measurements().await;
|
||||
|
||||
info!(
|
||||
"Finished performing verloc measurements. The next one will happen in {:?}",
|
||||
self.config.testing_interval
|
||||
);
|
||||
|
||||
tokio::select! {
|
||||
_ = sleep(self.config.testing_interval) => {},
|
||||
_ = self.shutdown_listener.recv() => {
|
||||
log::trace!("Shutdown received while sleeping");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("Verloc: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
enum MeasurementOutcome {
|
||||
Done,
|
||||
Shutdown,
|
||||
}
|
||||
@@ -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"] }
|
||||
@@ -1 +0,0 @@
|
||||
pub mod ns_api;
|
||||
@@ -157,6 +157,46 @@ impl NymNetworkDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[cfg(feature = "env")]
|
||||
pub fn export_to_env(self) {
|
||||
use crate::var_names;
|
||||
use std::env::set_var;
|
||||
|
||||
fn set_optional_var(var_name: &str, value: Option<String>) {
|
||||
if let Some(value) = value {
|
||||
set_var(var_name, value);
|
||||
}
|
||||
}
|
||||
|
||||
set_var(var_names::NETWORK_NAME, self.network_name);
|
||||
set_var(var_names::BECH32_PREFIX, self.chain_details.bech32_account_prefix);
|
||||
|
||||
set_var(var_names::MIX_DENOM, self.chain_details.mix_denom.base);
|
||||
set_var(var_names::MIX_DENOM_DISPLAY, self.chain_details.mix_denom.display);
|
||||
|
||||
set_var(var_names::STAKE_DENOM, self.chain_details.stake_denom.base);
|
||||
set_var(var_names::STAKE_DENOM_DISPLAY, self.chain_details.stake_denom.display);
|
||||
|
||||
set_var(var_names::DENOMS_EXPONENT, self.chain_details.mix_denom.display_exponent.to_string());
|
||||
|
||||
if let Some(e) = self.endpoints.first() {
|
||||
set_var(var_names::NYXD, e.nyxd_url.clone());
|
||||
set_optional_var(var_names::NYM_API, e.api_url.clone());
|
||||
set_optional_var(var_names::NYXD_WEBSOCKET, e.websocket_url.clone());
|
||||
}
|
||||
|
||||
set_optional_var(var_names::MIXNET_CONTRACT_ADDRESS, self.contracts.mixnet_contract_address);
|
||||
set_optional_var(var_names::VESTING_CONTRACT_ADDRESS, self.contracts.vesting_contract_address);
|
||||
set_optional_var(var_names::ECASH_CONTRACT_ADDRESS, self.contracts.ecash_contract_address);
|
||||
set_optional_var(var_names::GROUP_CONTRACT_ADDRESS, self.contracts.group_contract_address);
|
||||
set_optional_var(var_names::MULTISIG_CONTRACT_ADDRESS, self.contracts.multisig_contract_address);
|
||||
set_optional_var(var_names::COCONUT_DKG_CONTRACT_ADDRESS, self.contracts.coconut_dkg_contract_address);
|
||||
|
||||
set_optional_var(var_names::EXPLORER_API, self.explorer_api);
|
||||
set_optional_var(var_names::NYM_VPN_API, self.nym_vpn_api_url);
|
||||
}
|
||||
|
||||
pub fn default_gas_price_amount(&self) -> f64 {
|
||||
GAS_PRICE_AMOUNT
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ pub struct NodeTester<R> {
|
||||
|
||||
packet_size: PacketSize,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
deterministic_route_selection: bool,
|
||||
|
||||
/// Average delay a data packet is going to get delay at a single mixnode.
|
||||
average_packet_delay: Duration,
|
||||
|
||||
@@ -48,11 +51,13 @@ impl<R> NodeTester<R>
|
||||
where
|
||||
R: Rng + CryptoRng,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
rng: R,
|
||||
base_topology: NymTopology,
|
||||
self_address: Option<Recipient>,
|
||||
packet_size: PacketSize,
|
||||
deterministic_route_selection: bool,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
ack_key: Arc<AckKey>,
|
||||
@@ -62,6 +67,7 @@ where
|
||||
base_topology,
|
||||
self_address,
|
||||
packet_size,
|
||||
deterministic_route_selection,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
||||
@@ -289,10 +295,18 @@ where
|
||||
impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
|
||||
type Rng = R;
|
||||
|
||||
fn deterministic_route_selection(&self) -> bool {
|
||||
self.deterministic_route_selection
|
||||
}
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng {
|
||||
&mut self.rng
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn num_mix_hops(&self) -> u8 {
|
||||
self.num_mix_hops
|
||||
}
|
||||
@@ -304,8 +318,4 @@ impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
|
||||
fn average_ack_delay(&self) -> Duration {
|
||||
self.average_ack_delay
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,18 +324,6 @@ pub fn unchecked_aggregate_indices_signatures(
|
||||
_aggregate_indices_signatures(params, vk, signatures_shares, false)
|
||||
}
|
||||
|
||||
/// Generates parameters for the scheme setup.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `total_coins` - it is the number of coins in a freshly generated wallet. It is the public parameter of the scheme.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Parameters` struct containing group parameters, public key, the number of signatures (`total_coins`),
|
||||
/// and a map of signatures for each index `l`.
|
||||
///
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -264,7 +264,7 @@ impl<'b> Add<&'b VerificationKeyAuth> for VerificationKeyAuth {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<Scalar> for &'a VerificationKeyAuth {
|
||||
impl Mul<Scalar> for &VerificationKeyAuth {
|
||||
type Output = VerificationKeyAuth;
|
||||
|
||||
#[inline]
|
||||
@@ -530,6 +530,15 @@ impl From<KeyPairUser> for SecretKeyUser {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SecretKeyUser> for KeyPairUser {
|
||||
fn from(value: SecretKeyUser) -> Self {
|
||||
KeyPairUser {
|
||||
public_key: value.public_key(),
|
||||
secret_key: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPairUser {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
|
||||
@@ -984,7 +984,7 @@ pub struct SerialNumberRef<'a> {
|
||||
pub(crate) inner: &'a [G1Projective],
|
||||
}
|
||||
|
||||
impl<'a> SerialNumberRef<'a> {
|
||||
impl SerialNumberRef<'_> {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let ss_len = self.inner.len();
|
||||
let mut bytes: Vec<u8> = Vec::with_capacity(ss_len * 48);
|
||||
|
||||
@@ -206,10 +206,10 @@ impl Deref for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Scalar> for &'a PublicKey {
|
||||
impl<'a> Mul<&'a Scalar> for &PublicKey {
|
||||
type Output = G1Projective;
|
||||
|
||||
fn mul(self, rhs: &'b Scalar) -> Self::Output {
|
||||
fn mul(self, rhs: &'a Scalar) -> Self::Output {
|
||||
self.0 * rhs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ impl<'b> Add<&'b VerificationKey> for VerificationKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<Scalar> for &'a VerificationKey {
|
||||
impl Mul<Scalar> for &VerificationKey {
|
||||
type Output = VerificationKey;
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -64,7 +64,7 @@ impl<'de> Deserialize<'de> for Recipient {
|
||||
{
|
||||
struct RecipientVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for RecipientVisitor {
|
||||
impl Visitor<'_> for RecipientVisitor {
|
||||
type Value = Recipient;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Encoding and decoding node routing information.
|
||||
//!
|
||||
//! This module is responsible for encoding and decoding node routing information, so that
|
||||
//! they could be later put into an appropriate field in a sphinx header.
|
||||
//! Currently, that routing information is an IP address, but in principle it can be anything
|
||||
//! for as long as it's going to fit in the field.
|
||||
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx_types::{NodeAddressBytes, NODE_ADDRESS_LENGTH};
|
||||
|
||||
@@ -12,13 +19,6 @@ use thiserror::Error;
|
||||
pub type NodeIdentity = identity::PublicKey;
|
||||
pub const NODE_IDENTITY_SIZE: usize = identity::PUBLIC_KEY_LENGTH;
|
||||
|
||||
/// Encodoing and decoding node routing information.
|
||||
///
|
||||
/// This module is responsible for encoding and decoding node routing information, so that
|
||||
/// they could be later put into an appropriate field in a sphinx header.
|
||||
/// Currently, that routing information is an IP address, but in principle it can be anything
|
||||
/// for as long as it's going to fit in the field.
|
||||
|
||||
/// MAX_UNPADDED_LEN represents maximum length an unpadded address could have.
|
||||
/// In this case it's an ipv6 socket address (with version prefix)
|
||||
pub const MAX_NODE_ADDRESS_UNPADDED_LEN: usize = 19;
|
||||
|
||||
@@ -56,7 +56,7 @@ impl<'de> Deserialize<'de> for ReplySurb {
|
||||
{
|
||||
struct ReplySurbVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ReplySurbVisitor {
|
||||
impl Visitor<'_> for ReplySurbVisitor {
|
||||
type Value = ReplySurb;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
|
||||
|
||||
@@ -18,19 +18,13 @@ pub struct FramedNymPacket {
|
||||
}
|
||||
|
||||
impl FramedNymPacket {
|
||||
pub fn new(packet: NymPacket, packet_type: PacketType, use_legacy_version: bool) -> Self {
|
||||
pub fn new(packet: NymPacket, packet_type: PacketType) -> Self {
|
||||
// If this fails somebody is using the library in a super incorrect way, because they
|
||||
// already managed to somehow create a sphinx packet
|
||||
let packet_size = PacketSize::get_type(packet.len()).unwrap();
|
||||
|
||||
let use_legacy = if packet_type == PacketType::Outfox {
|
||||
false
|
||||
} else {
|
||||
use_legacy_version
|
||||
};
|
||||
|
||||
let header = Header {
|
||||
packet_version: PacketVersion::new(use_legacy),
|
||||
packet_version: PacketVersion::new(),
|
||||
packet_size,
|
||||
packet_type,
|
||||
};
|
||||
|
||||
@@ -13,12 +13,8 @@ pub enum PacketVersion {
|
||||
}
|
||||
|
||||
impl PacketVersion {
|
||||
pub fn new(use_legacy: bool) -> Self {
|
||||
if use_legacy {
|
||||
Self::new_legacy()
|
||||
} else {
|
||||
Self::new_versioned(CURRENT_PACKET_VERSION_NUMBER)
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Self::new_versioned(CURRENT_PACKET_VERSION_NUMBER)
|
||||
}
|
||||
|
||||
pub fn new_legacy() -> Self {
|
||||
|
||||
@@ -18,7 +18,7 @@ use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX
|
||||
use nym_sphinx_types::{Delay, NymPacket};
|
||||
use nym_topology::{NymTopology, NymTopologyError};
|
||||
use rand::{CryptoRng, Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
|
||||
use nym_sphinx_chunking::monitoring;
|
||||
use std::time::Duration;
|
||||
@@ -51,6 +51,7 @@ impl From<PreparedFragment> for MixPacket {
|
||||
pub trait FragmentPreparer {
|
||||
type Rng: CryptoRng + Rng;
|
||||
|
||||
fn deterministic_route_selection(&self) -> bool;
|
||||
fn rng(&mut self) -> &mut Self::Rng;
|
||||
fn nonce(&self) -> i32;
|
||||
fn num_mix_hops(&self) -> u8;
|
||||
@@ -201,9 +202,7 @@ pub trait FragmentPreparer {
|
||||
// could perform diffie-hellman with its own keys followed by a kdf to re-derive
|
||||
// the packet encryption key
|
||||
|
||||
let seed = fragment.seed().wrapping_mul(self.nonce());
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(seed as u64);
|
||||
|
||||
let fragment_header = fragment.header();
|
||||
let destination = packet_recipient.gateway();
|
||||
let hops = mix_hops.unwrap_or(self.num_mix_hops());
|
||||
monitoring::fragment_sent(&fragment, self.nonce(), *destination, hops);
|
||||
@@ -241,8 +240,18 @@ pub trait FragmentPreparer {
|
||||
};
|
||||
|
||||
// generate pseudorandom route for the packet
|
||||
log::trace!("Preparing chunk for sending with {} mix hops", hops);
|
||||
let route = topology.random_route_to_gateway(&mut rng, hops, destination)?;
|
||||
log::trace!("Preparing chunk for sending with {hops} mix hops");
|
||||
let route = if self.deterministic_route_selection() {
|
||||
log::trace!("using deterministic route selection");
|
||||
let seed = fragment_header.seed().wrapping_mul(self.nonce());
|
||||
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
|
||||
topology.random_route_to_gateway(&mut rng, hops, destination)?
|
||||
} else {
|
||||
log::trace!("using pseudorandom route selection");
|
||||
let mut rng = self.rng();
|
||||
topology.random_route_to_gateway(&mut rng, hops, destination)?
|
||||
};
|
||||
|
||||
let destination = packet_recipient.as_sphinx_destination();
|
||||
|
||||
// including set of delays
|
||||
@@ -313,6 +322,9 @@ pub struct MessagePreparer<R> {
|
||||
/// Instance of a cryptographically secure random number generator.
|
||||
rng: R,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
deterministic_route_selection: bool,
|
||||
|
||||
/// Address of this client which also represent an address to which all acknowledgements
|
||||
/// and surb-based are going to be sent.
|
||||
sender_address: Recipient,
|
||||
@@ -336,6 +348,7 @@ where
|
||||
{
|
||||
pub fn new(
|
||||
rng: R,
|
||||
deterministic_route_selection: bool,
|
||||
sender_address: Recipient,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
@@ -344,6 +357,7 @@ where
|
||||
let nonce = rng.gen();
|
||||
MessagePreparer {
|
||||
rng,
|
||||
deterministic_route_selection,
|
||||
sender_address,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
@@ -457,10 +471,18 @@ where
|
||||
impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
|
||||
type Rng = R;
|
||||
|
||||
fn deterministic_route_selection(&self) -> bool {
|
||||
self.deterministic_route_selection
|
||||
}
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng {
|
||||
&mut self.rng
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
self.nonce
|
||||
}
|
||||
|
||||
fn num_mix_hops(&self) -> u8 {
|
||||
self.num_mix_hops
|
||||
}
|
||||
@@ -472,10 +494,6 @@ impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
|
||||
fn average_ack_delay(&self) -> Duration {
|
||||
self.average_ack_delay
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
self.nonce
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -253,25 +253,15 @@ impl Socks5RequestContent {
|
||||
/// Deserialize the request type, connection id, destination address and port,
|
||||
/// and the request body from bytes.
|
||||
///
|
||||
// TODO: this was already inaccurate
|
||||
// /// Serialized bytes looks like this:
|
||||
// ///
|
||||
// /// --------------------------------------------------------------------------------------
|
||||
// /// request_flag | connection_id | address_length | remote_address_bytes | request_data |
|
||||
// /// 1 | 8 | 2 | address_length | ... |
|
||||
// /// --------------------------------------------------------------------------------------
|
||||
///
|
||||
/// The request_flag tells us whether this is a new connection request (`new_connect`),
|
||||
/// an already-established connection we should send up (`new_send`), or
|
||||
/// a request to close an established connection (`new_close`).
|
||||
|
||||
// connect:
|
||||
// RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR>
|
||||
//
|
||||
// send:
|
||||
// RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA
|
||||
// where DATA: SEQ || TRUE_DATA
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
|
||||
// each request needs to at least contain flag and ConnectionId
|
||||
if b.is_empty() {
|
||||
|
||||
@@ -26,3 +26,4 @@ nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-metrics = { path = "../nym-metrics" }
|
||||
nym-task = { path = "../task" }
|
||||
|
||||
|
||||
@@ -31,40 +31,12 @@ impl GatewayStatsReporter {
|
||||
/// Gateway Statistics events
|
||||
pub enum GatewayStatsEvent {
|
||||
/// Events in the lifecycle of an established client tunnel
|
||||
SessionStatsEvent(SessionEvent),
|
||||
}
|
||||
|
||||
impl GatewayStatsEvent {
|
||||
/// A new session between this gateway and the client remote has successfully opened
|
||||
pub fn new_session_start(client: DestinationAddressBytes) -> GatewayStatsEvent {
|
||||
GatewayStatsEvent::SessionStatsEvent(SessionEvent::SessionStart {
|
||||
start_time: OffsetDateTime::now_utc(),
|
||||
client,
|
||||
})
|
||||
}
|
||||
|
||||
/// An existing session with the client remote has ended
|
||||
pub fn new_session_stop(client: DestinationAddressBytes) -> GatewayStatsEvent {
|
||||
GatewayStatsEvent::SessionStatsEvent(SessionEvent::SessionStop {
|
||||
stop_time: OffsetDateTime::now_utc(),
|
||||
client,
|
||||
})
|
||||
}
|
||||
|
||||
/// A new ecash ticket has been added / requested
|
||||
pub fn new_ecash_ticket(
|
||||
client: DestinationAddressBytes,
|
||||
ticket_type: TicketType,
|
||||
) -> GatewayStatsEvent {
|
||||
GatewayStatsEvent::SessionStatsEvent(SessionEvent::EcashTicket {
|
||||
ticket_type,
|
||||
client,
|
||||
})
|
||||
}
|
||||
SessionStatsEvent(GatewaySessionEvent),
|
||||
}
|
||||
|
||||
/// Events in the lifecycle of an established client tunnel
|
||||
pub enum SessionEvent {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GatewaySessionEvent {
|
||||
/// A new session between this gateway and the client remote has successfully opened
|
||||
SessionStart {
|
||||
/// The timestamp of the session open event
|
||||
@@ -87,3 +59,32 @@ pub enum SessionEvent {
|
||||
client: DestinationAddressBytes,
|
||||
},
|
||||
}
|
||||
|
||||
impl GatewaySessionEvent {
|
||||
/// A new session between this gateway and the client remote has successfully opened
|
||||
pub fn new_session_start(client: DestinationAddressBytes) -> GatewaySessionEvent {
|
||||
GatewaySessionEvent::SessionStart {
|
||||
start_time: OffsetDateTime::now_utc(),
|
||||
client,
|
||||
}
|
||||
}
|
||||
|
||||
/// An existing session with the client remote has ended
|
||||
pub fn new_session_stop(client: DestinationAddressBytes) -> GatewaySessionEvent {
|
||||
GatewaySessionEvent::SessionStop {
|
||||
stop_time: OffsetDateTime::now_utc(),
|
||||
client,
|
||||
}
|
||||
}
|
||||
|
||||
/// A new ecash ticket has been added / requested
|
||||
pub fn new_ecash_ticket(
|
||||
client: DestinationAddressBytes,
|
||||
ticket_type: TicketType,
|
||||
) -> GatewaySessionEvent {
|
||||
GatewaySessionEvent::EcashTicket {
|
||||
ticket_type,
|
||||
client,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use sha2::Digest;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
/// Client specific statistics interfaces and events.
|
||||
pub mod clients;
|
||||
@@ -36,3 +36,7 @@ fn generate_stats_id<M: AsRef<[u8]>>(prefix: &str, id_seed: M) -> String {
|
||||
let output = hasher.finalize();
|
||||
format!("{:x}", output)
|
||||
}
|
||||
|
||||
pub fn hash_identifier<M: AsRef<[u8]>>(identifier: M) -> String {
|
||||
format!("{:x}", Sha256::digest(identifier))
|
||||
}
|
||||
|
||||
@@ -455,6 +455,10 @@ impl TaskClient {
|
||||
self.mode.set_should_not_signal_on_drop();
|
||||
}
|
||||
|
||||
pub fn rearm(&mut self) {
|
||||
self.mode.set_should_signal_on_drop();
|
||||
}
|
||||
|
||||
pub fn send_we_stopped(&mut self, err: SentError) {
|
||||
if self.mode.is_dummy() {
|
||||
return;
|
||||
@@ -482,7 +486,7 @@ impl Drop for TaskClient {
|
||||
if !self.mode.should_signal_on_drop() {
|
||||
self.log(
|
||||
Level::Trace,
|
||||
"the task client is getting dropped but inststructed to not signal: this is expected during client shutdown",
|
||||
"the task client is getting dropped but instructed to not signal: this is expected during client shutdown",
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
@@ -527,6 +531,14 @@ impl ClientOperatingMode {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_should_signal_on_drop(&mut self) {
|
||||
use ClientOperatingMode::{Dummy, Listening, ListeningButDontReportHalt};
|
||||
*self = match &self {
|
||||
ListeningButDontReportHalt | Listening => Listening,
|
||||
Dummy => Dummy,
|
||||
};
|
||||
}
|
||||
|
||||
fn set_should_not_signal_on_drop(&mut self) {
|
||||
use ClientOperatingMode::{Dummy, Listening, ListeningButDontReportHalt};
|
||||
*self = match &self {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "nym-verloc"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bytes = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
humantime = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "net", "rt-multi-thread", "io-util"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
thiserror = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
|
||||
nym-task = { path = "../task" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client" }
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerlocError {
|
||||
#[error("the received echo packet had unexpected size")]
|
||||
UnexpectedEchoPacketSize,
|
||||
|
||||
#[error("the received reply packet had unexpected size")]
|
||||
UnexpectedReplyPacketSize,
|
||||
|
||||
#[error("the received echo packet had malformed sender")]
|
||||
MalformedSenderIdentity,
|
||||
|
||||
#[error("the received echo packet had malformed signature")]
|
||||
MalformedEchoSignature,
|
||||
|
||||
#[error("the received reply packet had malformed signature")]
|
||||
MalformedReplySignature,
|
||||
|
||||
#[error("the received echo packet had invalid signature")]
|
||||
InvalidEchoSignature,
|
||||
|
||||
#[error("the received reply packet had invalid signature")]
|
||||
InvalidReplySignature,
|
||||
|
||||
#[error("could not establish connection to {identity} on {address}: {err}")]
|
||||
UnreachableNode {
|
||||
identity: String,
|
||||
address: SocketAddr,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to write echo packet to {identity} on {address}: {err}")]
|
||||
UnexpectedConnectionFailureWrite {
|
||||
identity: String,
|
||||
address: SocketAddr,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to read reply packet from {identity} on {address}: {err}")]
|
||||
UnexpectedConnectionFailureRead {
|
||||
identity: String,
|
||||
address: SocketAddr,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("timed out while trying to read reply packet from {identity} on {address}")]
|
||||
ConnectionReadTimeout {
|
||||
identity: String,
|
||||
address: SocketAddr,
|
||||
},
|
||||
|
||||
#[error("timed out while trying to write echo packet to {identity} on {address}")]
|
||||
ConnectionWriteTimeout {
|
||||
identity: String,
|
||||
address: SocketAddr,
|
||||
},
|
||||
|
||||
#[error("the received reply packet had an unexpected sequence number")]
|
||||
UnexpectedReplySequence,
|
||||
|
||||
#[error("shutdown signal received")]
|
||||
ShutdownReceived,
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
#![warn(clippy::todo)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
pub mod error;
|
||||
pub mod measurements;
|
||||
pub mod models;
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_validator_client::UserAgent;
|
||||
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
// by default all of those are overwritten by config data from nym-node directly
|
||||
const DEFAULT_VERLOC_PORT: u16 = 1790;
|
||||
const DEFAULT_PACKETS_PER_NODE: usize = 100;
|
||||
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
|
||||
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
|
||||
const DEFAULT_BATCH_SIZE: usize = 50;
|
||||
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
|
||||
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
/// Socket address of this node on which it will be listening for the measurement packets.
|
||||
pub listening_address: SocketAddr,
|
||||
|
||||
/// Specifies number of echo packets sent to each node during a measurement run.
|
||||
pub packets_per_node: usize,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
|
||||
pub packet_timeout: Duration,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the connection to get established.
|
||||
pub connection_timeout: Duration,
|
||||
|
||||
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
|
||||
pub delay_between_packets: Duration,
|
||||
|
||||
/// Specifies number of nodes being tested at once.
|
||||
pub tested_nodes_batch_size: usize,
|
||||
|
||||
/// Specifies delay between subsequent test runs.
|
||||
pub testing_interval: Duration,
|
||||
|
||||
/// Specifies delay between attempting to run the measurement again if the previous run failed
|
||||
/// due to being unable to get the list of nodes.
|
||||
pub retry_timeout: Duration,
|
||||
|
||||
/// URLs to the nym apis for obtaining network topology.
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
|
||||
/// User agent used for querying the nym-api
|
||||
pub user_agent: UserAgent,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn build(nym_api_urls: Vec<Url>, user_agent: impl Into<UserAgent>) -> ConfigBuilder {
|
||||
ConfigBuilder::new(nym_api_urls, user_agent)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct ConfigBuilder(Config);
|
||||
|
||||
impl ConfigBuilder {
|
||||
pub fn new(nym_api_urls: Vec<Url>, user_agent: impl Into<UserAgent>) -> ConfigBuilder {
|
||||
ConfigBuilder(Config {
|
||||
// '[::]:port'
|
||||
listening_address: SocketAddr::new(
|
||||
IpAddr::V6(Ipv6Addr::UNSPECIFIED),
|
||||
DEFAULT_VERLOC_PORT,
|
||||
),
|
||||
packets_per_node: DEFAULT_PACKETS_PER_NODE,
|
||||
packet_timeout: DEFAULT_PACKET_TIMEOUT,
|
||||
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
|
||||
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
|
||||
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
|
||||
testing_interval: DEFAULT_TESTING_INTERVAL,
|
||||
retry_timeout: DEFAULT_RETRY_TIMEOUT,
|
||||
nym_api_urls,
|
||||
user_agent: user_agent.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn listening_address(mut self, listening_address: SocketAddr) -> Self {
|
||||
self.0.listening_address = listening_address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn packets_per_node(mut self, packets_per_node: usize) -> Self {
|
||||
self.0.packets_per_node = packets_per_node;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn packet_timeout(mut self, packet_timeout: Duration) -> Self {
|
||||
self.0.packet_timeout = packet_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn connection_timeout(mut self, connection_timeout: Duration) -> Self {
|
||||
self.0.connection_timeout = connection_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn delay_between_packets(mut self, delay_between_packets: Duration) -> Self {
|
||||
self.0.delay_between_packets = delay_between_packets;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tested_nodes_batch_size(mut self, tested_nodes_batch_size: usize) -> Self {
|
||||
self.0.tested_nodes_batch_size = tested_nodes_batch_size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn testing_interval(mut self, testing_interval: Duration) -> Self {
|
||||
self.0.testing_interval = testing_interval;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn retry_timeout(mut self, retry_timeout: Duration) -> Self {
|
||||
self.0.retry_timeout = retry_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn nym_api_urls(mut self, nym_api_urls: Vec<Url>) -> Self {
|
||||
self.0.nym_api_urls = nym_api_urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Config {
|
||||
// panics here are fine as those are only ever constructed at the initial setup
|
||||
assert!(
|
||||
!self.0.nym_api_urls.is_empty(),
|
||||
"at least one validator endpoint must be provided",
|
||||
);
|
||||
self.0
|
||||
}
|
||||
}
|
||||
+21
-34
@@ -1,29 +1,29 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::verloc::error::RttError;
|
||||
use crate::verloc::packet::{EchoPacket, ReplyPacket};
|
||||
use crate::error::VerlocError;
|
||||
use crate::measurements::packet::{EchoPacket, ReplyPacket};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_task::TaskClient;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io, process};
|
||||
use std::{io, process};
|
||||
use thiserror::Error;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_util::codec::{Decoder, Encoder, Framed};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
pub(crate) struct PacketListener {
|
||||
pub struct PacketListener {
|
||||
address: SocketAddr,
|
||||
connection_handler: Arc<ConnectionHandler>,
|
||||
shutdown: TaskClient,
|
||||
}
|
||||
|
||||
impl PacketListener {
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
address: SocketAddr,
|
||||
identity: Arc<identity::KeyPair>,
|
||||
shutdown: TaskClient,
|
||||
@@ -37,13 +37,13 @@ impl PacketListener {
|
||||
}
|
||||
|
||||
impl PacketListener {
|
||||
pub(super) async fn run(self: Arc<Self>) {
|
||||
pub async fn run(self: Arc<Self>) {
|
||||
let listener = match TcpListener::bind(self.address).await {
|
||||
Ok(listener) => listener,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to bind to {} - {}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?",
|
||||
self.address, err
|
||||
"Failed to bind to {}: {err}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?",
|
||||
self.address
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ impl PacketListener {
|
||||
}
|
||||
},
|
||||
_ = shutdown_listener.recv() => {
|
||||
log::trace!("PacketListener: Received shutdown");
|
||||
trace!("PacketListener: Received shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,28 +137,18 @@ impl ConnectionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
enum EchoPacketCodecError {
|
||||
IoError(io::Error),
|
||||
PacketRecoveryError(RttError),
|
||||
#[error("encountered io error {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
|
||||
#[error("failed to correctly decode an echo packet: {0}")]
|
||||
PacketRecoveryError(Box<VerlocError>),
|
||||
}
|
||||
|
||||
impl Display for EchoPacketCodecError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
EchoPacketCodecError::IoError(err) => write!(f, "encountered io error - {err}"),
|
||||
EchoPacketCodecError::PacketRecoveryError(err) => {
|
||||
write!(f, "failed to correctly decode an echo packet - {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for EchoPacketCodecError {}
|
||||
|
||||
impl From<io::Error> for EchoPacketCodecError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
EchoPacketCodecError::IoError(err)
|
||||
impl From<VerlocError> for EchoPacketCodecError {
|
||||
fn from(value: VerlocError) -> Self {
|
||||
EchoPacketCodecError::PacketRecoveryError(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,10 +178,7 @@ impl Decoder for EchoPacketCodec {
|
||||
|
||||
let packet_bytes = src.split_to(EchoPacket::SIZE);
|
||||
|
||||
let echo_packet = match EchoPacket::try_from_bytes(&packet_bytes) {
|
||||
Ok(packet) => packet,
|
||||
Err(err) => return Err(EchoPacketCodecError::PacketRecoveryError(err)),
|
||||
};
|
||||
let echo_packet = EchoPacket::try_from_bytes(&packet_bytes)?;
|
||||
|
||||
// reserve enough bytes for the next frame
|
||||
src.reserve(EchoPacket::SIZE);
|
||||
@@ -0,0 +1,225 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::measurements::metrics::SharedVerlocStats;
|
||||
use crate::measurements::sender::TestedNode;
|
||||
use crate::measurements::{Config, PacketListener, PacketSender};
|
||||
use crate::models::VerlocNodeResult;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_task::TaskClient;
|
||||
use nym_validator_client::models::NymNodeDescription;
|
||||
use nym_validator_client::NymApiClient;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
pub struct VerlocMeasurer {
|
||||
config: Config,
|
||||
packet_sender: Arc<PacketSender>,
|
||||
packet_listener: Arc<PacketListener>,
|
||||
shutdown_listener: TaskClient,
|
||||
state: SharedVerlocStats,
|
||||
}
|
||||
|
||||
impl VerlocMeasurer {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
identity: Arc<identity::KeyPair>,
|
||||
shutdown_listener: TaskClient,
|
||||
) -> Self {
|
||||
VerlocMeasurer {
|
||||
packet_sender: Arc::new(PacketSender::new(
|
||||
Arc::clone(&identity),
|
||||
config.packets_per_node,
|
||||
config.packet_timeout,
|
||||
config.connection_timeout,
|
||||
config.delay_between_packets,
|
||||
shutdown_listener.clone().named("VerlocPacketSender"),
|
||||
)),
|
||||
packet_listener: Arc::new(PacketListener::new(
|
||||
config.listening_address,
|
||||
Arc::clone(&identity),
|
||||
shutdown_listener.clone().named("VerlocPacketListener"),
|
||||
)),
|
||||
shutdown_listener,
|
||||
config,
|
||||
state: SharedVerlocStats::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_shared_state(&mut self, state: SharedVerlocStats) {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
fn start_listening(&self) -> JoinHandle<()> {
|
||||
let packet_listener = Arc::clone(&self.packet_listener);
|
||||
tokio::spawn(packet_listener.run())
|
||||
}
|
||||
|
||||
async fn perform_measurement(&self, nodes_to_test: Vec<TestedNode>) -> MeasurementOutcome {
|
||||
trace!("Performing measurements");
|
||||
if nodes_to_test.is_empty() {
|
||||
debug!("there are no nodes to measure");
|
||||
return MeasurementOutcome::Done;
|
||||
}
|
||||
|
||||
let mut shutdown_listener = self.shutdown_listener.clone().named("VerlocMeasurement");
|
||||
shutdown_listener.disarm();
|
||||
|
||||
for chunk in nodes_to_test.chunks(self.config.tested_nodes_batch_size) {
|
||||
let mut chunk_results = Vec::with_capacity(chunk.len());
|
||||
|
||||
let mut measurement_chunk = chunk
|
||||
.iter()
|
||||
.map(|node| {
|
||||
let node = *node;
|
||||
let packet_sender = Arc::clone(&self.packet_sender);
|
||||
// TODO: there's a potential issue here. if we make the measurement go into separate
|
||||
// task, we risk biasing results with the bunch of context switches overhead
|
||||
// but if we don't do it, it will take ages to complete
|
||||
|
||||
// TODO: check performance difference when it's not spawned as a separate task
|
||||
tokio::spawn(async move {
|
||||
(
|
||||
packet_sender.send_packets_to_node(node).await,
|
||||
node.identity,
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>();
|
||||
|
||||
// exhaust the results
|
||||
while !shutdown_listener.is_shutdown() {
|
||||
tokio::select! {
|
||||
measurement_result = measurement_chunk.next() => {
|
||||
let Some(result) = measurement_result else {
|
||||
// if the stream has finished, it means we got everything we could have gotten
|
||||
break
|
||||
};
|
||||
|
||||
// if we receive JoinError it means the task failed to get executed, so either there's a bigger issue with tokio
|
||||
// or there was a panic inside the task itself. In either case, we should just terminate ourselves.
|
||||
let Ok(execution_result) = result else {
|
||||
error!("the verloc measurer has panicked!");
|
||||
continue
|
||||
};
|
||||
let identity = execution_result.1;
|
||||
|
||||
let measurement_result = match execution_result.0 {
|
||||
Err(err) => {
|
||||
debug!("Failed to perform measurement for {identity}: {err}");
|
||||
None
|
||||
}
|
||||
Ok(result) => Some(result),
|
||||
};
|
||||
chunk_results.push(VerlocNodeResult::new(identity, measurement_result));
|
||||
},
|
||||
_ = shutdown_listener.recv() => {
|
||||
trace!("Shutdown received while measuring");
|
||||
return MeasurementOutcome::Shutdown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the results vector with chunks as they become available (by default every 50 nodes)
|
||||
self.state.append_measurement_results(chunk_results).await;
|
||||
}
|
||||
|
||||
MeasurementOutcome::Done
|
||||
}
|
||||
|
||||
async fn get_list_of_nodes(&self) -> Option<Vec<NymNodeDescription>> {
|
||||
let mut api_endpoints = self.config.nym_api_urls.clone();
|
||||
api_endpoints.shuffle(&mut thread_rng());
|
||||
for api_endpoint in api_endpoints {
|
||||
let client = NymApiClient::new_with_user_agent(
|
||||
api_endpoint.clone(),
|
||||
self.config.user_agent.clone(),
|
||||
);
|
||||
match client.get_all_described_nodes().await {
|
||||
Ok(res) => return Some(res),
|
||||
Err(err) => {
|
||||
warn!("failed to get described nodes from {api_endpoint}: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
self.start_listening();
|
||||
|
||||
while !self.shutdown_listener.is_shutdown() {
|
||||
info!("Starting verloc measurements");
|
||||
// TODO: should we also measure gateways?
|
||||
|
||||
let Some(all_nodes) = self.get_list_of_nodes().await else {
|
||||
error!("failed to obtain list of all nodes from any available api endpoint");
|
||||
sleep(self.config.retry_timeout).await;
|
||||
continue;
|
||||
};
|
||||
|
||||
if all_nodes.is_empty() {
|
||||
warn!("it does not seem there are any nodes to measure...");
|
||||
sleep(self.config.retry_timeout).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
// we only care about address and identity
|
||||
let tested_nodes = all_nodes
|
||||
.into_iter()
|
||||
.filter_map(|node| {
|
||||
// try to parse the identity and host
|
||||
let node_identity = node.ed25519_identity_key();
|
||||
|
||||
let ip = node.description.host_information.ip_address.first()?;
|
||||
let verloc_port = node.description.verloc_port();
|
||||
let verloc_host = SocketAddr::new(*ip, verloc_port);
|
||||
|
||||
// TODO: possible problem in the future, this does name resolution and theoretically
|
||||
// if a lot of nodes maliciously mis-configured themselves, it might take a while to resolve them all
|
||||
// However, maybe it's not a problem as if they are misconfigured, they will eventually be
|
||||
// pushed out of the network and on top of that, verloc is done in separate task that runs
|
||||
// only every few hours.
|
||||
Some(TestedNode::new(verloc_host, node_identity))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// on start of each run remove old results
|
||||
self.state.start_new_measurements(tested_nodes.len()).await;
|
||||
|
||||
if let MeasurementOutcome::Shutdown = self.perform_measurement(tested_nodes).await {
|
||||
trace!("Shutting down after aborting measurements");
|
||||
break;
|
||||
}
|
||||
|
||||
// write current time to "run finished" field
|
||||
self.state.finish_measurements().await;
|
||||
|
||||
info!(
|
||||
"Finished performing verloc measurements. The next one will happen in {:?}",
|
||||
self.config.testing_interval
|
||||
);
|
||||
|
||||
tokio::select! {
|
||||
_ = sleep(self.config.testing_interval) => {},
|
||||
_ = self.shutdown_listener.recv() => {
|
||||
trace!("Shutdown received while sleeping");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace!("Verloc: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
enum MeasurementOutcome {
|
||||
Done,
|
||||
Shutdown,
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::{VerlocNodeResult, VerlocResultData};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SharedVerlocStats {
|
||||
inner: Arc<RwLock<VerlocStatsState>>,
|
||||
}
|
||||
|
||||
impl SharedVerlocStats {
|
||||
pub(crate) async fn start_new_measurements(&self, nodes_to_test: usize) {
|
||||
let mut guard = self.write().await;
|
||||
guard.previous_run_data = mem::take(&mut guard.current_run_data);
|
||||
guard.current_run_data.nodes_tested = nodes_to_test;
|
||||
}
|
||||
|
||||
pub(crate) async fn append_measurement_results(&self, mut new_data: Vec<VerlocNodeResult>) {
|
||||
let mut write_permit = self.write().await;
|
||||
write_permit.current_run_data.results.append(&mut new_data);
|
||||
// make sure the data always stays in order.
|
||||
// TODO: considering the front of the results is guaranteed to be sorted, should perhaps
|
||||
// a non-default sorting algorithm be used?
|
||||
write_permit.current_run_data.results.sort()
|
||||
}
|
||||
|
||||
pub(crate) async fn finish_measurements(&self) {
|
||||
self.write().await.current_run_data.run_finished = Some(OffsetDateTime::now_utc())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct VerlocStatsState {
|
||||
pub current_run_data: VerlocResultData,
|
||||
pub previous_run_data: VerlocResultData,
|
||||
}
|
||||
|
||||
impl SharedVerlocStats {
|
||||
pub async fn read(&self) -> RwLockReadGuard<'_, VerlocStatsState> {
|
||||
self.inner.read().await
|
||||
}
|
||||
|
||||
pub async fn write(&self) -> RwLockWriteGuard<'_, VerlocStatsState> {
|
||||
self.inner.write().await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod config;
|
||||
pub mod listener;
|
||||
pub mod measurer;
|
||||
pub mod metrics;
|
||||
pub mod packet;
|
||||
pub mod sender;
|
||||
|
||||
pub use config::{Config, ConfigBuilder};
|
||||
pub use listener::PacketListener;
|
||||
pub use measurer::VerlocMeasurer;
|
||||
pub use metrics::{SharedVerlocStats, VerlocStatsState};
|
||||
pub use packet::{EchoPacket, ReplyPacket};
|
||||
pub use sender::PacketSender;
|
||||
+25
-23
@@ -1,20 +1,20 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::verloc::error::RttError;
|
||||
use nym_crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH};
|
||||
use crate::error::VerlocError;
|
||||
use nym_crypto::asymmetric::ed25519::{self, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH};
|
||||
|
||||
pub(crate) struct EchoPacket {
|
||||
pub struct EchoPacket {
|
||||
sequence_number: u64,
|
||||
sender: identity::PublicKey,
|
||||
sender: ed25519::PublicKey,
|
||||
|
||||
signature: identity::Signature,
|
||||
signature: ed25519::Signature,
|
||||
}
|
||||
|
||||
impl EchoPacket {
|
||||
pub(crate) const SIZE: usize = 8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH;
|
||||
|
||||
pub(crate) fn new(sequence_number: u64, keys: &identity::KeyPair) -> Self {
|
||||
pub(crate) fn new(sequence_number: u64, keys: &ed25519::KeyPair) -> Self {
|
||||
let bytes_to_sign = sequence_number
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
@@ -42,20 +42,22 @@ impl EchoPacket {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<Self, RttError> {
|
||||
pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<Self, VerlocError> {
|
||||
if bytes.len() != Self::SIZE {
|
||||
return Err(RttError::UnexpectedEchoPacketSize);
|
||||
return Err(VerlocError::UnexpectedEchoPacketSize);
|
||||
}
|
||||
|
||||
// SAFETY: we have ensured our packet has correct size
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let sequence_number = u64::from_be_bytes(bytes[..8].try_into().unwrap());
|
||||
let sender = identity::PublicKey::from_bytes(&bytes[8..8 + PUBLIC_KEY_LENGTH])
|
||||
.map_err(|_| RttError::MalformedSenderIdentity)?;
|
||||
let signature = identity::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH..])
|
||||
.map_err(|_| RttError::MalformedEchoSignature)?;
|
||||
let sender = ed25519::PublicKey::from_bytes(&bytes[8..8 + PUBLIC_KEY_LENGTH])
|
||||
.map_err(|_| VerlocError::MalformedSenderIdentity)?;
|
||||
let signature = ed25519::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH..])
|
||||
.map_err(|_| VerlocError::MalformedEchoSignature)?;
|
||||
|
||||
sender
|
||||
.verify(&bytes[..Self::SIZE - SIGNATURE_LENGTH], &signature)
|
||||
.map_err(|_| RttError::InvalidEchoSignature)?;
|
||||
.map_err(|_| VerlocError::InvalidEchoSignature)?;
|
||||
|
||||
Ok(EchoPacket {
|
||||
sequence_number,
|
||||
@@ -64,7 +66,7 @@ impl EchoPacket {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn construct_reply(self, private_key: &identity::PrivateKey) -> ReplyPacket {
|
||||
pub(crate) fn construct_reply(self, private_key: &ed25519::PrivateKey) -> ReplyPacket {
|
||||
let bytes = self.to_bytes();
|
||||
let signature = private_key.sign(bytes);
|
||||
ReplyPacket {
|
||||
@@ -74,9 +76,9 @@ impl EchoPacket {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ReplyPacket {
|
||||
pub struct ReplyPacket {
|
||||
base_packet: EchoPacket,
|
||||
signature: identity::Signature,
|
||||
signature: ed25519::Signature,
|
||||
}
|
||||
|
||||
impl ReplyPacket {
|
||||
@@ -96,21 +98,21 @@ impl ReplyPacket {
|
||||
|
||||
pub(crate) fn try_from_bytes(
|
||||
bytes: &[u8],
|
||||
remote_identity: &identity::PublicKey,
|
||||
) -> Result<Self, RttError> {
|
||||
remote_ed25519: &ed25519::PublicKey,
|
||||
) -> Result<Self, VerlocError> {
|
||||
if bytes.len() != Self::SIZE {
|
||||
return Err(RttError::UnexpectedReplyPacketSize);
|
||||
return Err(VerlocError::UnexpectedReplyPacketSize);
|
||||
}
|
||||
|
||||
let base_packet =
|
||||
EchoPacket::try_from_bytes(&bytes[..8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH])?;
|
||||
let signature =
|
||||
identity::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH..])
|
||||
.map_err(|_| RttError::MalformedReplySignature)?;
|
||||
ed25519::Signature::from_bytes(&bytes[8 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH..])
|
||||
.map_err(|_| VerlocError::MalformedReplySignature)?;
|
||||
|
||||
remote_identity
|
||||
remote_ed25519
|
||||
.verify(&bytes[..Self::SIZE - SIGNATURE_LENGTH], &signature)
|
||||
.map_err(|_| RttError::InvalidReplySignature)?;
|
||||
.map_err(|_| VerlocError::InvalidReplySignature)?;
|
||||
|
||||
Ok(ReplyPacket {
|
||||
base_packet,
|
||||
+50
-46
@@ -1,11 +1,10 @@
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::verloc::error::RttError;
|
||||
use crate::verloc::packet::{EchoPacket, ReplyPacket};
|
||||
use log::*;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_node_http_api::state::metrics::VerlocMeasurement;
|
||||
use crate::error::VerlocError;
|
||||
use crate::measurements::packet::{EchoPacket, ReplyPacket};
|
||||
use crate::models::VerlocMeasurement;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_task::TaskClient;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::net::SocketAddr;
|
||||
@@ -15,15 +14,16 @@ use std::{fmt, io};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct TestedNode {
|
||||
pub(crate) address: SocketAddr,
|
||||
pub(crate) identity: identity::PublicKey,
|
||||
pub(crate) identity: ed25519::PublicKey,
|
||||
}
|
||||
|
||||
impl TestedNode {
|
||||
pub(crate) fn new(address: SocketAddr, identity: identity::PublicKey) -> Self {
|
||||
pub(crate) fn new(address: SocketAddr, identity: ed25519::PublicKey) -> Self {
|
||||
TestedNode { address, identity }
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,8 @@ impl fmt::Display for TestedNode {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PacketSender {
|
||||
identity: Arc<identity::KeyPair>,
|
||||
pub struct PacketSender {
|
||||
identity: Arc<ed25519::KeyPair>,
|
||||
// timeout for receiving before sending new one
|
||||
packets_per_node: usize,
|
||||
packet_timeout: Duration,
|
||||
@@ -49,8 +49,8 @@ pub(crate) struct PacketSender {
|
||||
}
|
||||
|
||||
impl PacketSender {
|
||||
pub(super) fn new(
|
||||
identity: Arc<identity::KeyPair>,
|
||||
pub fn new(
|
||||
identity: Arc<ed25519::KeyPair>,
|
||||
packets_per_node: usize,
|
||||
packet_timeout: Duration,
|
||||
connection_timeout: Duration,
|
||||
@@ -82,7 +82,7 @@ impl PacketSender {
|
||||
pub(super) async fn send_packets_to_node(
|
||||
self: Arc<Self>,
|
||||
tested_node: TestedNode,
|
||||
) -> Result<VerlocMeasurement, RttError> {
|
||||
) -> Result<VerlocMeasurement, VerlocError> {
|
||||
let mut shutdown_listener = self.shutdown_listener.fork(tested_node.address.to_string());
|
||||
shutdown_listener.disarm();
|
||||
|
||||
@@ -93,16 +93,18 @@ impl PacketSender {
|
||||
.await
|
||||
{
|
||||
Err(_timeout) => {
|
||||
return Err(RttError::UnreachableNode(
|
||||
tested_node.identity.to_base58_string(),
|
||||
io::ErrorKind::TimedOut.into(),
|
||||
))
|
||||
return Err(VerlocError::UnreachableNode {
|
||||
identity: tested_node.identity.to_string(),
|
||||
err: io::ErrorKind::TimedOut.into(),
|
||||
address: tested_node.address,
|
||||
})
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
return Err(RttError::UnreachableNode(
|
||||
tested_node.identity.to_base58_string(),
|
||||
return Err(VerlocError::UnreachableNode {
|
||||
identity: tested_node.identity.to_string(),
|
||||
err,
|
||||
))
|
||||
address: tested_node.address,
|
||||
})
|
||||
}
|
||||
Ok(Ok(conn)) => conn,
|
||||
};
|
||||
@@ -121,33 +123,34 @@ impl PacketSender {
|
||||
write = tokio::time::timeout(self.packet_timeout, conn.write_all(packet_bytes.as_ref())) => {
|
||||
match write {
|
||||
Err(_timeout) => {
|
||||
let identity_string = tested_node.identity.to_base58_string();
|
||||
let identity = tested_node.identity;
|
||||
debug!(
|
||||
"failed to write echo packet to {} within {:?}. Stopping the test.",
|
||||
identity_string, self.packet_timeout
|
||||
"failed to write echo packet to {identity} within {:?}. Stopping the test.",
|
||||
self.packet_timeout
|
||||
);
|
||||
return Err(RttError::UnexpectedConnectionFailureWrite(
|
||||
identity_string,
|
||||
io::ErrorKind::TimedOut.into(),
|
||||
));
|
||||
return Err(VerlocError::UnexpectedConnectionFailureWrite{
|
||||
identity: identity.to_string(),
|
||||
err:io::ErrorKind::TimedOut.into(),
|
||||
address: tested_node.address
|
||||
});
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
let identity_string = tested_node.identity.to_base58_string();
|
||||
let identity = tested_node.identity;
|
||||
debug!(
|
||||
"failed to write echo packet to {} - {}. Stopping the test.",
|
||||
identity_string, err
|
||||
"failed to write echo packet to {identity}: {err}. Stopping the test.",
|
||||
);
|
||||
return Err(RttError::UnexpectedConnectionFailureWrite(
|
||||
identity_string,
|
||||
return Err(VerlocError::UnexpectedConnectionFailureWrite{
|
||||
identity: identity.to_string(),
|
||||
err,
|
||||
));
|
||||
address: tested_node.address
|
||||
});
|
||||
}
|
||||
Ok(Ok(_)) => {}
|
||||
}
|
||||
},
|
||||
_ = shutdown_listener.recv() => {
|
||||
log::trace!("PacketSender: Received shutdown while sending");
|
||||
return Err(RttError::ShutdownReceived);
|
||||
trace!("PacketSender: Received shutdown while sending");
|
||||
return Err(VerlocError::ShutdownReceived);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -156,15 +159,15 @@ impl PacketSender {
|
||||
let reply_packet_future = async {
|
||||
let mut buf = [0u8; ReplyPacket::SIZE];
|
||||
if let Err(err) = conn.read_exact(&mut buf).await {
|
||||
let identity = tested_node.identity;
|
||||
debug!(
|
||||
"failed to read reply packet from {} - {}. Stopping the test.",
|
||||
tested_node.identity.to_base58_string(),
|
||||
err
|
||||
"failed to read reply packet from {identity}: {err}. Stopping the test.",
|
||||
);
|
||||
return Err(RttError::UnexpectedConnectionFailureRead(
|
||||
tested_node.identity.to_base58_string(),
|
||||
return Err(VerlocError::UnexpectedConnectionFailureRead {
|
||||
identity: identity.to_string(),
|
||||
err,
|
||||
));
|
||||
address: tested_node.address,
|
||||
});
|
||||
}
|
||||
ReplyPacket::try_from_bytes(&buf, &tested_node.identity)
|
||||
};
|
||||
@@ -180,15 +183,16 @@ impl PacketSender {
|
||||
"failed to receive reply to our echo packet within {:?}. Stopping the test",
|
||||
self.packet_timeout
|
||||
);
|
||||
return Err(RttError::ConnectionReadTimeout(
|
||||
tested_node.identity.to_base58_string(),
|
||||
));
|
||||
return Err(VerlocError::ConnectionReadTimeout{
|
||||
identity: tested_node.identity.to_string(),
|
||||
address: tested_node.address
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
_ = shutdown_listener.recv() => {
|
||||
log::trace!("PacketSender: Received shutdown while waiting for reply");
|
||||
return Err(RttError::ShutdownReceived);
|
||||
trace!("PacketSender: Received shutdown while waiting for reply");
|
||||
return Err(VerlocError::ShutdownReceived);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -198,7 +202,7 @@ impl PacketSender {
|
||||
// we have received the previous one
|
||||
if reply_packet.base_sequence_number() != seq {
|
||||
debug!("Received reply packet with invalid sequence number! Got {} expected {}. Stopping the test", reply_packet.base_sequence_number(), seq);
|
||||
return Err(RttError::UnexpectedReplySequence);
|
||||
return Err(VerlocError::UnexpectedReplySequence);
|
||||
}
|
||||
|
||||
let time_taken = tokio::time::Instant::now().duration_since(start);
|
||||
@@ -0,0 +1,225 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::asymmetric::ed25519::{self};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VerlocResultData {
|
||||
pub nodes_tested: usize,
|
||||
|
||||
pub run_started: OffsetDateTime,
|
||||
|
||||
pub run_finished: Option<OffsetDateTime>,
|
||||
|
||||
pub results: Vec<VerlocNodeResult>,
|
||||
}
|
||||
|
||||
impl Default for VerlocResultData {
|
||||
fn default() -> Self {
|
||||
VerlocResultData {
|
||||
nodes_tested: 0,
|
||||
run_started: OffsetDateTime::now_utc(),
|
||||
run_finished: None,
|
||||
results: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VerlocResultData {
|
||||
pub fn run_finished(&self) -> bool {
|
||||
self.run_finished.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct VerlocNodeResult {
|
||||
pub node_identity: ed25519::PublicKey,
|
||||
|
||||
pub latest_measurement: Option<VerlocMeasurement>,
|
||||
}
|
||||
|
||||
impl VerlocNodeResult {
|
||||
pub fn new(
|
||||
node_identity: ed25519::PublicKey,
|
||||
latest_measurement: Option<VerlocMeasurement>,
|
||||
) -> Self {
|
||||
VerlocNodeResult {
|
||||
node_identity,
|
||||
latest_measurement,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for VerlocNodeResult {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for VerlocNodeResult {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// if both have measurement, compare measurements
|
||||
// then if only one have measurement, prefer that one
|
||||
// completely ignore identity as it makes no sense to order by it
|
||||
if let Some(self_measurement) = &self.latest_measurement {
|
||||
if let Some(other_measurement) = &other.latest_measurement {
|
||||
self_measurement.cmp(other_measurement)
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
} else if other.latest_measurement.is_some() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct VerlocMeasurement {
|
||||
/// Minimum RTT duration it took to receive an echo packet.
|
||||
pub minimum: Duration,
|
||||
|
||||
/// Average RTT duration it took to receive the echo packets.
|
||||
pub mean: Duration,
|
||||
|
||||
/// Maximum RTT duration it took to receive an echo packet.
|
||||
pub maximum: Duration,
|
||||
|
||||
/// The standard deviation of the RTT duration it took to receive the echo packets.
|
||||
pub standard_deviation: Duration,
|
||||
}
|
||||
|
||||
impl VerlocMeasurement {
|
||||
pub fn new(raw_results: &[Duration]) -> Self {
|
||||
let minimum = raw_results.iter().min().copied().unwrap_or_default();
|
||||
let maximum = raw_results.iter().max().copied().unwrap_or_default();
|
||||
|
||||
let mean = Self::duration_mean(raw_results);
|
||||
let standard_deviation = Self::duration_standard_deviation(raw_results, mean);
|
||||
|
||||
VerlocMeasurement {
|
||||
minimum,
|
||||
mean,
|
||||
maximum,
|
||||
standard_deviation,
|
||||
}
|
||||
}
|
||||
|
||||
fn duration_mean(data: &[Duration]) -> Duration {
|
||||
if data.is_empty() {
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
let sum = data.iter().sum::<Duration>();
|
||||
let count = data.len() as u32;
|
||||
|
||||
sum / count
|
||||
}
|
||||
|
||||
fn duration_standard_deviation(data: &[Duration], mean: Duration) -> Duration {
|
||||
if data.is_empty() {
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
let variance_micros = data
|
||||
.iter()
|
||||
.map(|&value| {
|
||||
// make sure we don't underflow
|
||||
let diff = if mean > value {
|
||||
mean - value
|
||||
} else {
|
||||
value - mean
|
||||
};
|
||||
// we don't need nanos precision
|
||||
let diff_micros = diff.as_micros();
|
||||
diff_micros * diff_micros
|
||||
})
|
||||
.sum::<u128>()
|
||||
/ data.len() as u128;
|
||||
|
||||
// we shouldn't really overflow as our differences shouldn't be larger than couple seconds at the worst possible case scenario
|
||||
let std_deviation_micros = (variance_micros as f64).sqrt() as u64;
|
||||
Duration::from_micros(std_deviation_micros)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VerlocMeasurement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"rtt min/avg/max/mdev = {} / {} / {} / {}",
|
||||
humantime::format_duration(self.minimum),
|
||||
humantime::format_duration(self.mean),
|
||||
humantime::format_duration(self.maximum),
|
||||
humantime::format_duration(self.standard_deviation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for VerlocMeasurement {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for VerlocMeasurement {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// minimum value is most important, then look at standard deviation, then mean and finally maximum
|
||||
let min_cmp = self.minimum.cmp(&other.minimum);
|
||||
if min_cmp != Ordering::Equal {
|
||||
return min_cmp;
|
||||
}
|
||||
let std_dev_cmp = self.standard_deviation.cmp(&other.standard_deviation);
|
||||
if std_dev_cmp != Ordering::Equal {
|
||||
return std_dev_cmp;
|
||||
}
|
||||
let std_dev_cmp = self.mean.cmp(&other.mean);
|
||||
if std_dev_cmp != Ordering::Equal {
|
||||
return std_dev_cmp;
|
||||
}
|
||||
self.maximum.cmp(&other.maximum)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sorting_vec_of_verlocs() {
|
||||
let some_identity =
|
||||
ed25519::PublicKey::from_base58_string("Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt")
|
||||
.unwrap();
|
||||
let no_measurement = VerlocNodeResult::new(some_identity, None);
|
||||
let low_min = VerlocNodeResult::new(
|
||||
some_identity,
|
||||
Some(VerlocMeasurement {
|
||||
minimum: Duration::from_millis(42),
|
||||
mean: Duration::from_millis(43),
|
||||
maximum: Duration::from_millis(44),
|
||||
standard_deviation: Duration::from_millis(45),
|
||||
}),
|
||||
);
|
||||
let higher_min = VerlocNodeResult::new(
|
||||
some_identity,
|
||||
Some(VerlocMeasurement {
|
||||
minimum: Duration::from_millis(420),
|
||||
mean: Duration::from_millis(430),
|
||||
maximum: Duration::from_millis(440),
|
||||
standard_deviation: Duration::from_millis(450),
|
||||
}),
|
||||
);
|
||||
|
||||
let mut vec_verloc = vec![no_measurement, low_min, no_measurement, higher_min];
|
||||
vec_verloc.sort();
|
||||
|
||||
let expected_sorted = vec![low_min, higher_min, no_measurement, no_measurement];
|
||||
assert_eq!(expected_sorted, vec_verloc);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user