Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0cf93d2b9d | |||
| 5d3a5a022f | |||
| 93a995f011 | |||
| 7bc6268b40 | |||
| 2ace2926d2 | |||
| bb6d7769ca | |||
| 616b52604a | |||
| 298c52d650 | |||
| 97f64f2687 | |||
| dc098dfe15 | |||
| 1b8a929ff5 | |||
| 72a4624ace | |||
| 12cd1d26a4 | |||
| e5e7ddb0b6 | |||
| 98165966ad | |||
| 5a07b73375 | |||
| d1f702c4aa | |||
| c20c7147f8 | |||
| 06956226ad | |||
| b06091e548 | |||
| f3bf5d080b | |||
| e06d442e95 | |||
| fef9e4ad78 | |||
| fc79f739d4 | |||
| 6ee8ccbeaa | |||
| cfebd14655 | |||
| 4851614375 | |||
| ec502f46f0 | |||
| 841fb81d24 | |||
| a9e62889c3 | |||
| 074d705448 | |||
| 4a9a5579c4 | |||
| 96180275f8 | |||
| ab20260a2f | |||
| ec1c564c2b | |||
| bdf97bcbd6 | |||
| 889d464e98 | |||
| 56206433e6 | |||
| 8e9d01c47b | |||
| 842158124d | |||
| 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 | |||
| c75e0fdb83 | |||
| 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
|
||||
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
|
||||
@@ -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.5
|
||||
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
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
uses: mikefarah/yq@v4.44.5
|
||||
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,10 @@ 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
|
||||
|
||||
Generated
+193
-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]]
|
||||
@@ -430,6 +430,14 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "autodoc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger 0.11.5",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.20"
|
||||
@@ -573,7 +581,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1140,7 +1148,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1790,7 +1798,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1948,7 +1956,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.11.1",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1970,7 +1978,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
||||
dependencies = [
|
||||
"darling_core 0.20.9",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2021,7 +2029,7 @@ dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2064,7 +2072,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2097,7 +2105,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2165,7 +2173,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2354,6 +2362,19 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime 2.1.0",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "envy"
|
||||
version = "0.4.2"
|
||||
@@ -2428,7 +2449,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.5.20",
|
||||
@@ -2531,7 +2552,7 @@ dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2765,7 +2786,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2860,7 +2881,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3248,6 +3269,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 +3849,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 +3977,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3961,7 +3988,7 @@ checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3974,7 +4001,7 @@ dependencies = [
|
||||
"macroific_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4128,6 +4155,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 +4473,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4451,7 +4493,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4701,7 +4743,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.44"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -4752,12 +4794,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",
|
||||
@@ -4782,7 +4826,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.44"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -4849,6 +4893,7 @@ dependencies = [
|
||||
"nym-gateway-requests",
|
||||
"nym-id",
|
||||
"nym-metrics",
|
||||
"nym-mixnet-client",
|
||||
"nym-network-defaults",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-pemstore",
|
||||
@@ -5007,16 +5052,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"
|
||||
@@ -5451,7 +5486,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",
|
||||
@@ -5543,8 +5578,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",
|
||||
@@ -5555,7 +5593,6 @@ dependencies = [
|
||||
name = "nym-gateway-storage"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bincode",
|
||||
"defguard_wireguard_rs",
|
||||
"log",
|
||||
@@ -5748,11 +5785,11 @@ name = "nym-mixnet-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"nym-sphinx",
|
||||
"nym-task",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5779,35 +5816,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"
|
||||
@@ -5816,11 +5824,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",
|
||||
@@ -5828,7 +5834,6 @@ dependencies = [
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-types",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"rand",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -5878,6 +5883,7 @@ dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-client-core",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"nym-sdk",
|
||||
@@ -5899,7 +5905,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.45"
|
||||
version = "1.1.46"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -5950,9 +5956,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.1.11"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"axum 0.7.7",
|
||||
"axum-extra",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"cargo_metadata 0.18.1",
|
||||
@@ -5960,6 +5969,10 @@ dependencies = [
|
||||
"clap 4.5.20",
|
||||
"colored",
|
||||
"cupid",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"headers",
|
||||
"human-repr",
|
||||
"humantime-serde",
|
||||
"ipnetwork 0.20.0",
|
||||
"nym-authenticator",
|
||||
@@ -5968,63 +5981,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]]
|
||||
@@ -6056,30 +6066,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.2"
|
||||
version = "1.0.0-rc.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.7",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"clap 4.5.20",
|
||||
"cosmwasm-std",
|
||||
@@ -6087,11 +6090,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",
|
||||
@@ -6103,6 +6109,7 @@ dependencies = [
|
||||
"strum 0.26.3",
|
||||
"strum_macros 0.26.4",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-http",
|
||||
@@ -6114,6 +6121,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"
|
||||
@@ -6306,7 +6328,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.44"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -6808,6 +6830,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"
|
||||
@@ -6886,8 +6927,10 @@ dependencies = [
|
||||
"nym-task",
|
||||
"nym-wireguard-types",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
@@ -6908,7 +6951,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7221,7 +7264,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7308,7 +7351,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7349,7 +7392,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7498,7 +7541,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"env_logger 0.7.1",
|
||||
"log",
|
||||
]
|
||||
|
||||
@@ -7564,14 +7607,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",
|
||||
]
|
||||
@@ -7584,7 +7627,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
@@ -7647,7 +7690,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7880,7 +7923,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8107,7 +8150,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rocket_http",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"unicode-xid",
|
||||
"version_check",
|
||||
]
|
||||
@@ -8233,7 +8276,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -8289,9 +8332,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",
|
||||
@@ -8482,7 +8525,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals 0.29.1",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8514,7 +8557,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8646,7 +8689,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8657,7 +8700,7 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8668,7 +8711,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8730,7 +8773,7 @@ checksum = "aafbefbe175fa9bf03ca83ef89beecff7d2a95aaacd5732325b90ac8c3bd7b90"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8751,7 +8794,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8802,7 +8845,7 @@ dependencies = [
|
||||
"darling 0.20.9",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9401,7 +9444,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9444,9 +9487,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",
|
||||
@@ -9752,7 +9795,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9883,7 +9926,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10162,7 +10205,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10326,7 +10369,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@@ -10353,7 +10396,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals 0.28.0",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10556,7 +10599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10588,7 +10631,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"toml 0.5.11",
|
||||
"uniffi_build",
|
||||
"uniffi_meta",
|
||||
@@ -10720,7 +10763,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -10759,7 +10802,7 @@ checksum = "17e82ab96c5a55263b5bed151b8426410d93aa909a453acdbd4b6792b5af7d64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10770,7 +10813,7 @@ checksum = "86b8338dc3c9526011ffaa2aa6bd60ddfda9d49d2123108690755c6e34844212"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"utoipauto-core",
|
||||
]
|
||||
|
||||
@@ -10877,7 +10920,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -10911,7 +10954,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -10945,7 +10988,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11473,7 +11516,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11493,7 +11536,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+10
-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",
|
||||
@@ -99,13 +98,12 @@ members = [
|
||||
"common/wasm/utils",
|
||||
"common/wireguard",
|
||||
"common/wireguard-types",
|
||||
# "documentation/autodoc",
|
||||
"documentation/autodoc",
|
||||
"explorer-api",
|
||||
"explorer-api/explorer-api-requests",
|
||||
"explorer-api/explorer-client",
|
||||
"gateway",
|
||||
"integrations/bity",
|
||||
"mixnode",
|
||||
"sdk/ffi/cpp",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/shared",
|
||||
@@ -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",
|
||||
@@ -150,22 +149,21 @@ members = [
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/testnet-manager",
|
||||
"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 +262,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.44"
|
||||
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.44"
|
||||
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" }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -5,8 +5,9 @@ use std::fmt;
|
||||
|
||||
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,
|
||||
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@ log.workspace = true
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
|
||||
tokio-stream = { workspace = true }
|
||||
time = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
nym-authenticator-requests = { path = "../authenticator-requests" }
|
||||
nym-credential-verification = { path = "../credential-verification" }
|
||||
|
||||
@@ -16,7 +16,7 @@ pub enum Error {
|
||||
MissingClientBandwidthEntry,
|
||||
|
||||
#[error("{0}")]
|
||||
GatewayStorage(#[from] nym_gateway_storage::error::StorageError),
|
||||
GatewayStorage(#[from] nym_gateway_storage::error::GatewayStorageError),
|
||||
|
||||
#[error("{0}")]
|
||||
SystemTime(#[from] std::time::SystemTimeError),
|
||||
|
||||
@@ -7,19 +7,22 @@
|
||||
// #![warn(clippy::unwrap_used)]
|
||||
|
||||
use defguard_wireguard_rs::WGApi;
|
||||
#[cfg(target_os = "linux")]
|
||||
use defguard_wireguard_rs::{host::Peer, key::Key, net::IpAddrMask};
|
||||
use nym_crypto::asymmetric::encryption::KeyPair;
|
||||
#[cfg(target_os = "linux")]
|
||||
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
|
||||
use nym_wireguard_types::Config;
|
||||
use peer_controller::PeerControlRequest;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use defguard_wireguard_rs::{host::Peer, key::Key, net::IpAddrMask};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
|
||||
|
||||
pub(crate) mod error;
|
||||
pub mod peer_controller;
|
||||
pub mod peer_handle;
|
||||
pub mod peer_storage_manager;
|
||||
|
||||
pub struct WgApiWrapper {
|
||||
inner: WGApi,
|
||||
@@ -80,8 +83,8 @@ pub struct WireguardData {
|
||||
|
||||
/// Start wireguard device
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>(
|
||||
storage: St,
|
||||
pub async fn start_wireguard(
|
||||
storage: nym_gateway_storage::GatewayStorage,
|
||||
all_peers: Vec<nym_gateway_storage::models::WireguardPeer>,
|
||||
task_client: nym_task::TaskClient,
|
||||
wireguard_data: WireguardData,
|
||||
@@ -92,6 +95,7 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
|
||||
use peer_controller::PeerController;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::info;
|
||||
|
||||
let ifname = String::from(WG_TUN_BASE_NAME);
|
||||
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), false)?;
|
||||
@@ -118,8 +122,9 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
|
||||
storage
|
||||
.insert_wireguard_peer(peer, bandwidth_manager.is_some())
|
||||
.await?;
|
||||
peer_bandwidth_managers.insert(peer.public_key.clone(), bandwidth_manager);
|
||||
peer_bandwidth_managers.insert(peer.public_key.clone(), (bandwidth_manager, peer.clone()));
|
||||
}
|
||||
|
||||
wg_api.create_interface()?;
|
||||
let interface_config = InterfaceConfiguration {
|
||||
name: ifname.clone(),
|
||||
@@ -129,6 +134,11 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
|
||||
peers,
|
||||
mtu: None,
|
||||
};
|
||||
info!(
|
||||
"attempting to configure wireguard interface '{ifname}': address={}, port={}",
|
||||
interface_config.address, interface_config.port
|
||||
);
|
||||
|
||||
wg_api.configure_interface(&interface_config)?;
|
||||
std::process::Command::new("ip")
|
||||
.args([
|
||||
|
||||
@@ -7,6 +7,7 @@ use defguard_wireguard_rs::{
|
||||
WireguardInterfaceApi,
|
||||
};
|
||||
use futures::channel::oneshot;
|
||||
use log::info;
|
||||
use nym_authenticator_requests::latest::registration::{
|
||||
RemainingBandwidthData, BANDWIDTH_CAP_PER_DAY,
|
||||
};
|
||||
@@ -14,15 +15,15 @@ use nym_credential_verification::{
|
||||
bandwidth_storage_manager::BandwidthStorageManager, BandwidthFlushingBehaviourConfig,
|
||||
ClientBandwidth,
|
||||
};
|
||||
use nym_gateway_storage::Storage;
|
||||
use nym_gateway_storage::GatewayStorage;
|
||||
use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
use tokio_stream::{wrappers::IntervalStream, StreamExt};
|
||||
|
||||
use crate::peer_handle::PeerHandle;
|
||||
use crate::WgApiWrapper;
|
||||
use crate::{error::Error, peer_handle::SharedBandwidthStorageManager};
|
||||
use crate::{peer_handle::PeerHandle, peer_storage_manager::PeerStorageManager};
|
||||
|
||||
pub enum PeerControlRequest {
|
||||
AddPeer {
|
||||
@@ -62,24 +63,24 @@ pub struct QueryBandwidthControlResponse {
|
||||
pub bandwidth_data: Option<RemainingBandwidthData>,
|
||||
}
|
||||
|
||||
pub struct PeerController<St: Storage + Clone + 'static> {
|
||||
storage: St,
|
||||
pub struct PeerController {
|
||||
storage: GatewayStorage,
|
||||
// used to receive commands from individual handles too
|
||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||
request_rx: mpsc::Receiver<PeerControlRequest>,
|
||||
wg_api: Arc<WgApiWrapper>,
|
||||
host_information: Arc<RwLock<Host>>,
|
||||
bw_storage_managers: HashMap<Key, Option<SharedBandwidthStorageManager<St>>>,
|
||||
bw_storage_managers: HashMap<Key, Option<SharedBandwidthStorageManager>>,
|
||||
timeout_check_interval: IntervalStream,
|
||||
task_client: nym_task::TaskClient,
|
||||
}
|
||||
|
||||
impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
impl PeerController {
|
||||
pub fn new(
|
||||
storage: St,
|
||||
storage: GatewayStorage,
|
||||
wg_api: Arc<WgApiWrapper>,
|
||||
initial_host_information: Host,
|
||||
bw_storage_managers: HashMap<Key, Option<SharedBandwidthStorageManager<St>>>,
|
||||
bw_storage_managers: HashMap<Key, (Option<SharedBandwidthStorageManager>, Peer)>,
|
||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||
request_rx: mpsc::Receiver<PeerControlRequest>,
|
||||
task_client: nym_task::TaskClient,
|
||||
@@ -88,11 +89,16 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
tokio::time::interval(DEFAULT_PEER_TIMEOUT_CHECK),
|
||||
);
|
||||
let host_information = Arc::new(RwLock::new(initial_host_information));
|
||||
for (public_key, bandwidth_storage_manager) in bw_storage_managers.iter() {
|
||||
let mut handle = PeerHandle::new(
|
||||
for (public_key, (bandwidth_storage_manager, peer)) in bw_storage_managers.iter() {
|
||||
let peer_storage_manager = PeerStorageManager::new(
|
||||
storage.clone(),
|
||||
peer.clone(),
|
||||
bandwidth_storage_manager.is_some(),
|
||||
);
|
||||
let mut handle = PeerHandle::new(
|
||||
public_key.clone(),
|
||||
host_information.clone(),
|
||||
peer_storage_manager,
|
||||
bandwidth_storage_manager.clone(),
|
||||
request_tx.clone(),
|
||||
&task_client,
|
||||
@@ -103,6 +109,10 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
}
|
||||
});
|
||||
}
|
||||
let bw_storage_managers = bw_storage_managers
|
||||
.into_iter()
|
||||
.map(|(k, (m, _))| (k, m))
|
||||
.collect();
|
||||
|
||||
PeerController {
|
||||
storage,
|
||||
@@ -149,9 +159,9 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
}
|
||||
|
||||
pub async fn generate_bandwidth_manager(
|
||||
storage: St,
|
||||
storage: GatewayStorage,
|
||||
public_key: &Key,
|
||||
) -> Result<Option<BandwidthStorageManager<St>>, Error> {
|
||||
) -> Result<Option<BandwidthStorageManager>, Error> {
|
||||
if let Some(client_id) = storage
|
||||
.get_wireguard_peer(&public_key.to_string())
|
||||
.await?
|
||||
@@ -184,10 +194,15 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
Self::generate_bandwidth_manager(self.storage.clone(), &peer.public_key)
|
||||
.await?
|
||||
.map(|bw_m| Arc::new(RwLock::new(bw_m)));
|
||||
let mut handle = PeerHandle::new(
|
||||
let peer_storage_manager = PeerStorageManager::new(
|
||||
self.storage.clone(),
|
||||
peer.clone(),
|
||||
bandwidth_storage_manager.is_some(),
|
||||
);
|
||||
let mut handle = PeerHandle::new(
|
||||
peer.public_key.clone(),
|
||||
self.host_information.clone(),
|
||||
peer_storage_manager,
|
||||
bandwidth_storage_manager.clone(),
|
||||
self.request_tx.clone(),
|
||||
&self.task_client,
|
||||
@@ -243,6 +258,7 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
info!("started wireguard peer controller");
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = self.timeout_check_interval.next() => {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::peer_controller::PeerControlRequest;
|
||||
use crate::peer_storage_manager::PeerStorageManager;
|
||||
use defguard_wireguard_rs::host::Peer;
|
||||
use defguard_wireguard_rs::{host::Host, key::Key};
|
||||
use futures::channel::oneshot;
|
||||
use nym_authenticator_requests::latest::registration::BANDWIDTH_CAP_PER_DAY;
|
||||
use nym_credential_verification::bandwidth_storage_manager::BandwidthStorageManager;
|
||||
use nym_gateway_storage::models::WireguardPeer;
|
||||
use nym_gateway_storage::Storage;
|
||||
use nym_task::TaskClient;
|
||||
use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK;
|
||||
use std::sync::Arc;
|
||||
@@ -17,26 +17,26 @@ use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
use tokio_stream::{wrappers::IntervalStream, StreamExt};
|
||||
|
||||
pub(crate) type SharedBandwidthStorageManager<St> = Arc<RwLock<BandwidthStorageManager<St>>>;
|
||||
pub(crate) type SharedBandwidthStorageManager = Arc<RwLock<BandwidthStorageManager>>;
|
||||
const AUTO_REMOVE_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 30); // 30 days
|
||||
|
||||
pub struct PeerHandle<St> {
|
||||
storage: St,
|
||||
pub struct PeerHandle {
|
||||
public_key: Key,
|
||||
host_information: Arc<RwLock<Host>>,
|
||||
bandwidth_storage_manager: Option<SharedBandwidthStorageManager<St>>,
|
||||
peer_storage_manager: PeerStorageManager,
|
||||
bandwidth_storage_manager: Option<SharedBandwidthStorageManager>,
|
||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||
timeout_check_interval: IntervalStream,
|
||||
task_client: TaskClient,
|
||||
startup_timestamp: SystemTime,
|
||||
}
|
||||
|
||||
impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
impl PeerHandle {
|
||||
pub fn new(
|
||||
storage: St,
|
||||
public_key: Key,
|
||||
host_information: Arc<RwLock<Host>>,
|
||||
bandwidth_storage_manager: Option<SharedBandwidthStorageManager<St>>,
|
||||
peer_storage_manager: PeerStorageManager,
|
||||
bandwidth_storage_manager: Option<SharedBandwidthStorageManager>,
|
||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||
task_client: &TaskClient,
|
||||
) -> Self {
|
||||
@@ -46,9 +46,9 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
let mut task_client = task_client.fork(format!("peer-{public_key}"));
|
||||
task_client.disarm();
|
||||
PeerHandle {
|
||||
storage,
|
||||
public_key,
|
||||
host_information,
|
||||
peer_storage_manager,
|
||||
bandwidth_storage_manager,
|
||||
request_tx,
|
||||
timeout_check_interval,
|
||||
@@ -84,16 +84,19 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
.ok_or(Error::InconsistentConsumedBytes)?
|
||||
.try_into()
|
||||
.map_err(|_| Error::InconsistentConsumedBytes)?;
|
||||
if spent_bandwidth > 0
|
||||
&& bandwidth_manager
|
||||
if spent_bandwidth > 0 {
|
||||
self.peer_storage_manager.update_trx(kernel_peer);
|
||||
if bandwidth_manager
|
||||
.write()
|
||||
.await
|
||||
.try_use_bandwidth(spent_bandwidth)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
let success = self.remove_peer().await?;
|
||||
return Ok(!success);
|
||||
{
|
||||
let success = self.remove_peer().await?;
|
||||
self.peer_storage_manager.remove_peer();
|
||||
return Ok(!success);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if SystemTime::now().duration_since(self.startup_timestamp)? >= AUTO_REMOVE_AFTER {
|
||||
@@ -132,7 +135,7 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
// the host information hasn't beed updated yet
|
||||
continue;
|
||||
};
|
||||
let Some(storage_peer) = self.storage.get_wireguard_peer(&self.public_key.to_string()).await? else {
|
||||
let Some(storage_peer) = self.peer_storage_manager.get_peer() else {
|
||||
log::debug!("Peer {:?} not in storage anymore, shutting down handle", self.public_key);
|
||||
return Ok(());
|
||||
};
|
||||
@@ -141,12 +144,18 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
return Ok(());
|
||||
} else {
|
||||
// Update storage values
|
||||
self.storage.insert_wireguard_peer(&kernel_peer, self.bandwidth_storage_manager.is_some()).await?;
|
||||
self.peer_storage_manager.sync_storage_peer().await?;
|
||||
}
|
||||
}
|
||||
|
||||
_ = self.task_client.recv() => {
|
||||
log::trace!("PeerHandle: Received shutdown");
|
||||
if let Some(bandwidth_manager) = &self.bandwidth_storage_manager {
|
||||
if let Err(e) = bandwidth_manager.write().await.sync_storage_bandwidth().await {
|
||||
log::error!("Storage sync failed - {e}, unaccounted bandwidth might have been consumed");
|
||||
}
|
||||
}
|
||||
log::trace!("PeerHandle: Finished shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use defguard_wireguard_rs::host::Peer;
|
||||
use nym_gateway_storage::models::WireguardPeer;
|
||||
use nym_gateway_storage::GatewayStorage;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
const DEFAULT_PEER_MAX_FLUSHING_RATE: Duration = Duration::from_secs(60 * 60 * 24); // 24h
|
||||
const DEFAULT_PEER_MAX_DELTA_FLUSHING_AMOUNT: u64 = 512 * 1024 * 1024; // 512MB
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PeerFlushingBehaviourConfig {
|
||||
/// Defines maximum delay between peer information being flushed to the persistent storage.
|
||||
pub peer_max_flushing_rate: Duration,
|
||||
|
||||
/// Defines a maximum change in peer before it gets flushed to the persistent storage.
|
||||
pub peer_max_delta_flushing_amount: u64,
|
||||
}
|
||||
|
||||
impl Default for PeerFlushingBehaviourConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
peer_max_flushing_rate: DEFAULT_PEER_MAX_FLUSHING_RATE,
|
||||
peer_max_delta_flushing_amount: DEFAULT_PEER_MAX_DELTA_FLUSHING_AMOUNT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PeerStorageManager {
|
||||
pub(crate) storage: GatewayStorage,
|
||||
pub(crate) peer_information: Option<PeerInformation>,
|
||||
pub(crate) cfg: PeerFlushingBehaviourConfig,
|
||||
pub(crate) with_client_id: bool,
|
||||
}
|
||||
|
||||
impl PeerStorageManager {
|
||||
pub(crate) fn new(storage: GatewayStorage, peer: Peer, with_client_id: bool) -> Self {
|
||||
let peer_information = Some(PeerInformation::new(peer));
|
||||
Self {
|
||||
storage,
|
||||
peer_information,
|
||||
cfg: PeerFlushingBehaviourConfig::default(),
|
||||
with_client_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_peer(&self) -> Option<WireguardPeer> {
|
||||
self.peer_information
|
||||
.as_ref()
|
||||
.map(|p| p.peer.clone().into())
|
||||
}
|
||||
|
||||
pub(crate) fn remove_peer(&mut self) {
|
||||
self.peer_information = None;
|
||||
}
|
||||
|
||||
pub(crate) fn update_trx(&mut self, kernel_peer: &Peer) {
|
||||
if let Some(peer_information) = self.peer_information.as_mut() {
|
||||
peer_information.update_trx_bytes(kernel_peer.tx_bytes, kernel_peer.rx_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn sync_storage_peer(&mut self) -> Result<(), Error> {
|
||||
let Some(peer_information) = self.peer_information.as_mut() else {
|
||||
return Ok(());
|
||||
};
|
||||
if !peer_information.should_sync(self.cfg) {
|
||||
return Ok(());
|
||||
}
|
||||
if self
|
||||
.storage
|
||||
.get_wireguard_peer(&peer_information.peer().public_key.to_string())
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
self.peer_information = None;
|
||||
return Ok(());
|
||||
}
|
||||
self.storage
|
||||
.insert_wireguard_peer(peer_information.peer(), self.with_client_id)
|
||||
.await?;
|
||||
|
||||
peer_information.resync_peer_with_storage();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct PeerInformation {
|
||||
pub(crate) peer: Peer,
|
||||
pub(crate) last_synced: OffsetDateTime,
|
||||
|
||||
pub(crate) bytes_delta_since_sync: u64,
|
||||
}
|
||||
|
||||
impl PeerInformation {
|
||||
pub fn new(peer: Peer) -> PeerInformation {
|
||||
PeerInformation {
|
||||
peer,
|
||||
last_synced: OffsetDateTime::now_utc(),
|
||||
bytes_delta_since_sync: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn should_sync(&self, cfg: PeerFlushingBehaviourConfig) -> bool {
|
||||
if self.bytes_delta_since_sync >= cfg.peer_max_delta_flushing_amount {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.last_synced + cfg.peer_max_flushing_rate < OffsetDateTime::now_utc()
|
||||
&& self.bytes_delta_since_sync != 0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn peer(&self) -> &Peer {
|
||||
&self.peer
|
||||
}
|
||||
|
||||
pub(crate) fn update_trx_bytes(&mut self, tx_bytes: u64, rx_bytes: u64) {
|
||||
self.bytes_delta_since_sync += tx_bytes.saturating_sub(self.peer.tx_bytes)
|
||||
+ rx_bytes.saturating_sub(self.peer.rx_bytes);
|
||||
self.peer.tx_bytes = tx_bytes;
|
||||
self.peer.rx_bytes = rx_bytes;
|
||||
}
|
||||
|
||||
pub(crate) fn resync_peer_with_storage(&mut self) {
|
||||
self.bytes_delta_since_sync = 0;
|
||||
self.last_synced = OffsetDateTime::now_utc();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
```sh
|
||||
Show build information of this binary
|
||||
|
||||
Usage: nym-api build-info [OPTIONS]
|
||||
|
||||
Options:
|
||||
-o, --output <OUTPUT> [default: text] [possible values: text, json]
|
||||
-h, --help Print help
|
||||
```
|
||||
@@ -1,13 +0,0 @@
|
||||
```sh
|
||||
|
||||
Binary Name: nym-api
|
||||
Build Timestamp: 2024-10-09T13:56:14.428750844Z
|
||||
Build Version: 1.1.42
|
||||
Commit SHA: fac373c1db4fa5389ba61de7943c77023467bccb
|
||||
Commit Date: 2024-10-09T14:59:40.000000000+02:00
|
||||
Commit Branch: max/new-docs-framework
|
||||
rustc Version: 1.80.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
|
||||
```
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
These docs are autogenerated by the [`autodocs`](https://github.com/nymtech/nym/tree/max/new-docs-framework/documentation/autodoc) script.
|
||||
```sh
|
||||
[2m2024-10-29T09:51:44.008469Z[0m [32m INFO[0m [2mnym-api/src/main.rs[0m[2m:[0m[2m41[0m[2m:[0m Starting nym api...
|
||||
Usage: nym-api [OPTIONS] <COMMAND>
|
||||
|
||||
Commands:
|
||||
@@ -24,7 +23,6 @@ Options:
|
||||
|
||||
### `init`
|
||||
```sh
|
||||
[2m2024-10-29T09:51:44.013292Z[0m [32m INFO[0m [2mnym-api/src/main.rs[0m[2m:[0m[2m41[0m[2m:[0m Starting nym api...
|
||||
Initialise a Nym Api instance with persistent config.toml file
|
||||
|
||||
Usage: nym-api init [OPTIONS]
|
||||
@@ -54,7 +52,6 @@ Options:
|
||||
|
||||
### `run`
|
||||
```sh
|
||||
[2m2024-10-29T09:51:44.032688Z[0m [32m INFO[0m [2mnym-api/src/main.rs[0m[2m:[0m[2m41[0m[2m:[0m Starting nym api...
|
||||
Run the Nym Api with provided configuration optionally overriding set parameters
|
||||
|
||||
Usage: nym-api run [OPTIONS]
|
||||
@@ -84,7 +81,6 @@ Options:
|
||||
|
||||
### `build-info`
|
||||
```sh
|
||||
[2m2024-10-29T09:51:44.037915Z[0m [32m INFO[0m [2mnym-api/src/main.rs[0m[2m:[0m[2m41[0m[2m:[0m Starting nym api...
|
||||
Show build information of this binary
|
||||
|
||||
Usage: nym-api build-info [OPTIONS]
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
```sh
|
||||
Initialise a Nym Api instance with persistent config.toml file
|
||||
|
||||
Usage: nym-api init [OPTIONS]
|
||||
|
||||
Options:
|
||||
--id <ID>
|
||||
Id of the nym-api we want to initialise. if unspecified, a default value will be used. default: "default" [default: default]
|
||||
-m, --enable-monitor
|
||||
Specifies whether network monitoring is enabled on this API default: false
|
||||
-r, --enable-rewarding
|
||||
Specifies whether network rewarding is enabled on this API default: false
|
||||
--nyxd-validator <NYXD_VALIDATOR>
|
||||
Endpoint to nyxd instance used for contract information. default: http://localhost:26657
|
||||
--mnemonic <MNEMONIC>
|
||||
Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions default: None
|
||||
--enable-zk-nym
|
||||
Flag to indicate whether credential signer authority is enabled on this API default: false
|
||||
--announce-address <ANNOUNCE_ADDRESS>
|
||||
Announced address that is going to be put in the DKG contract where zk-nym clients will connect to obtain their credentials default: None
|
||||
--monitor-credentials-mode
|
||||
Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
|
||||
-h, --help
|
||||
Print help
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
```sh
|
||||
Show build information of this binary
|
||||
|
||||
Usage: nym-client build-info [OPTIONS]
|
||||
|
||||
Options:
|
||||
-o, --output <OUTPUT> [default: text] [possible values: text, json]
|
||||
-h, --help Print help
|
||||
```
|
||||
@@ -1,13 +0,0 @@
|
||||
```sh
|
||||
|
||||
Binary Name: nym-client
|
||||
Build Timestamp: 2024-10-09T13:56:14.428750844Z
|
||||
Build Version: 1.1.39
|
||||
Commit SHA: fac373c1db4fa5389ba61de7943c77023467bccb
|
||||
Commit Date: 2024-10-09T14:59:40.000000000+02:00
|
||||
Commit Branch: max/new-docs-framework
|
||||
rustc Version: 1.80.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
|
||||
```
|
||||
@@ -1,11 +0,0 @@
|
||||
```sh
|
||||
Generate shell completions
|
||||
|
||||
Usage: nym-client completions <SHELL>
|
||||
|
||||
Arguments:
|
||||
<SHELL> [possible values: bash, elvish, fish, power-shell, zsh]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
```
|
||||
@@ -1,8 +0,0 @@
|
||||
```sh
|
||||
Generate Fig specification
|
||||
|
||||
Usage: nym-client generate-fig-spec
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
```
|
||||
@@ -1,585 +0,0 @@
|
||||
```sh
|
||||
const completion: Fig.Spec = {
|
||||
name: "nym-native-client",
|
||||
description: "Implementation of the Nym Client",
|
||||
subcommands: [
|
||||
{
|
||||
name: "init",
|
||||
description: "Initialise a Nym client. Do this first!",
|
||||
options: [
|
||||
{
|
||||
name: "--id",
|
||||
description: "Id of client we want to create config for",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--gateway",
|
||||
description: "Id of the gateway we are going to connect to",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "gateway",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--nyxd-urls",
|
||||
description: "Comma separated list of rest endpoints of the nyxd validators",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "nyxd_urls",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--nym-apis",
|
||||
description: "Comma separated list of rest endpoints of the API validators",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "nym_apis",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--custom-mixnet",
|
||||
description: "Path to .json file containing custom network specification",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "custom_mixnet",
|
||||
isOptional: true,
|
||||
template: "filepaths",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--enabled-credentials-mode",
|
||||
description: "Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "enabled_credentials_mode",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"true",
|
||||
"false",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--disable-socket",
|
||||
description: "Whether to not start the websocket",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "disable_socket",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"true",
|
||||
"false",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-p", "--port"],
|
||||
description: "Port for the socket (if applicable) to listen on in all subsequent runs",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "port",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--host",
|
||||
description: "Ip for the socket (if applicable) to listen for requests",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "host",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-o", "--output"],
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "output",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"text",
|
||||
"json",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--force-tls-gateway",
|
||||
description: "Specifies whether the client will attempt to enforce tls connection to the desired gateway",
|
||||
},
|
||||
{
|
||||
name: "--latency-based-selection",
|
||||
description: "Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly",
|
||||
exclusiveOn: [
|
||||
"--gateway",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "--fastmode",
|
||||
description: "Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init",
|
||||
},
|
||||
{
|
||||
name: "--no-cover",
|
||||
description: "Disable loop cover traffic and the Poisson rate limiter (for debugging only)",
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "run",
|
||||
description: "Run the Nym client with provided configuration client optionally overriding set parameters",
|
||||
options: [
|
||||
{
|
||||
name: "--id",
|
||||
description: "Id of client we want to create config for",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--gateway",
|
||||
description: "Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "gateway",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--nyxd-urls",
|
||||
description: "Comma separated list of rest endpoints of the nyxd validators",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "nyxd_urls",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--nym-apis",
|
||||
description: "Comma separated list of rest endpoints of the API validators",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "nym_apis",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--custom-mixnet",
|
||||
description: "Path to .json file containing custom network specification",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "custom_mixnet",
|
||||
isOptional: true,
|
||||
template: "filepaths",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--enabled-credentials-mode",
|
||||
description: "Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "enabled_credentials_mode",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"true",
|
||||
"false",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--disable-socket",
|
||||
description: "Whether to not start the websocket",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "disable_socket",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"true",
|
||||
"false",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-p", "--port"],
|
||||
description: "Port for the socket to listen on",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "port",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--host",
|
||||
description: "Ip for the socket (if applicable) to listen for requests",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "host",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--fastmode",
|
||||
description: "Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init",
|
||||
},
|
||||
{
|
||||
name: "--no-cover",
|
||||
description: "Disable loop cover traffic and the Poisson rate limiter (for debugging only)",
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "import-credential",
|
||||
description: "Import a pre-generated credential",
|
||||
options: [
|
||||
{
|
||||
name: "--id",
|
||||
description: "Id of client that is going to import the credential",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--credential-data",
|
||||
description: "Explicitly provide the encoded credential data (as base58)",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "credential_data",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--credential-path",
|
||||
description: "Specifies the path to file containing binary credential data",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "credential_path",
|
||||
isOptional: true,
|
||||
template: "filepaths",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--version",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "version",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "list-gateways",
|
||||
description: "List all registered with gateways",
|
||||
options: [
|
||||
{
|
||||
name: "--id",
|
||||
description: "Id of client we want to list gateways for",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-o", "--output"],
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "output",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"text",
|
||||
"json",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "add-gateway",
|
||||
description: "Add new gateway to this client",
|
||||
options: [
|
||||
{
|
||||
name: "--id",
|
||||
description: "Id of client we want to add gateway for",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--gateway-id",
|
||||
description: "Explicitly specify id of the gateway to register with. If unspecified, a random gateway will be chosen instead",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "gateway_id",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--nym-apis",
|
||||
description: "Comma separated list of rest endpoints of the API validators",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "nym_apis",
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--custom-mixnet",
|
||||
description: "Path to .json file containing custom network specification",
|
||||
hidden: true,
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "custom_mixnet",
|
||||
isOptional: true,
|
||||
template: "filepaths",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-o", "--output"],
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "output",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"text",
|
||||
"json",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--force-tls-gateway",
|
||||
description: "Specifies whether the client will attempt to enforce tls connection to the desired gateway",
|
||||
},
|
||||
{
|
||||
name: "--latency-based-selection",
|
||||
description: "Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly",
|
||||
exclusiveOn: [
|
||||
"--gateway-id",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "--set-active",
|
||||
description: "Specify whether this new gateway should be set as the active one",
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "switch-gateway",
|
||||
description: "Change the currently active gateway. Note that you must have already registered with the new gateway!",
|
||||
options: [
|
||||
{
|
||||
name: "--id",
|
||||
description: "Id of client we want to list gateways for",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--gateway-id",
|
||||
description: "Id of the gateway we want to switch to",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "gateway_id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "show-ticketbooks",
|
||||
description: "Display information associated with the imported ticketbooks,",
|
||||
options: [
|
||||
{
|
||||
name: "--id",
|
||||
description: "Id of client that is going to display the ticketbook information",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-o", "--output"],
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "output",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"text",
|
||||
"json",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "build-info",
|
||||
description: "Show build information of this binary",
|
||||
options: [
|
||||
{
|
||||
name: ["-o", "--output"],
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "output",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
"text",
|
||||
"json",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "completions",
|
||||
description: "Generate shell completions",
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
name: "shell",
|
||||
suggestions: [
|
||||
"bash",
|
||||
"elvish",
|
||||
"fish",
|
||||
"power-shell",
|
||||
"zsh",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "generate-fig-spec",
|
||||
description: "Generate Fig specification",
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "help",
|
||||
description: "Print this message or the help of the given subcommand(s)",
|
||||
subcommands: [
|
||||
{
|
||||
name: "init",
|
||||
description: "Initialise a Nym client. Do this first!",
|
||||
},
|
||||
{
|
||||
name: "run",
|
||||
description: "Run the Nym client with provided configuration client optionally overriding set parameters",
|
||||
},
|
||||
{
|
||||
name: "import-credential",
|
||||
description: "Import a pre-generated credential",
|
||||
},
|
||||
{
|
||||
name: "list-gateways",
|
||||
description: "List all registered with gateways",
|
||||
},
|
||||
{
|
||||
name: "add-gateway",
|
||||
description: "Add new gateway to this client",
|
||||
},
|
||||
{
|
||||
name: "switch-gateway",
|
||||
description: "Change the currently active gateway. Note that you must have already registered with the new gateway!",
|
||||
},
|
||||
{
|
||||
name: "show-ticketbooks",
|
||||
description: "Display information associated with the imported ticketbooks,",
|
||||
},
|
||||
{
|
||||
name: "build-info",
|
||||
description: "Show build information of this binary",
|
||||
},
|
||||
{
|
||||
name: "completions",
|
||||
description: "Generate shell completions",
|
||||
},
|
||||
{
|
||||
name: "generate-fig-spec",
|
||||
description: "Generate Fig specification",
|
||||
},
|
||||
{
|
||||
name: "help",
|
||||
description: "Print this message or the help of the given subcommand(s)",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: ["-c", "--config-env-file"],
|
||||
description: "Path pointing to an env file that configures the client",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "config_env_file",
|
||||
isOptional: true,
|
||||
template: "filepaths",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--no-banner",
|
||||
description: "Flag used for disabling the printed banner in tty",
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default completion;
|
||||
```
|
||||
@@ -1,15 +0,0 @@
|
||||
```sh
|
||||
Import a pre-generated credential
|
||||
|
||||
Usage: nym-client import-credential --id <ID> <--credential-data <CREDENTIAL_DATA>|--credential-path <CREDENTIAL_PATH>>
|
||||
|
||||
Options:
|
||||
--id <ID>
|
||||
Id of client that is going to import the credential
|
||||
--credential-data <CREDENTIAL_DATA>
|
||||
Explicitly provide the encoded credential data (as base58)
|
||||
--credential-path <CREDENTIAL_PATH>
|
||||
Specifies the path to file containing binary credential data
|
||||
-h, --help
|
||||
Print help
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
```sh
|
||||
Initialise a Nym client. Do this first!
|
||||
|
||||
Usage: nym-client init [OPTIONS] --id <ID>
|
||||
|
||||
Options:
|
||||
--id <ID>
|
||||
Id of client we want to create config for
|
||||
--gateway <GATEWAY>
|
||||
Id of the gateway we are going to connect to
|
||||
--force-tls-gateway
|
||||
Specifies whether the client will attempt to enforce tls connection to the desired gateway
|
||||
--latency-based-selection
|
||||
Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly
|
||||
--nym-apis <NYM_APIS>
|
||||
Comma separated list of rest endpoints of the API validators
|
||||
--disable-socket <DISABLE_SOCKET>
|
||||
Whether to not start the websocket [possible values: true, false]
|
||||
-p, --port <PORT>
|
||||
Port for the socket (if applicable) to listen on in all subsequent runs
|
||||
--host <HOST>
|
||||
Ip for the socket (if applicable) to listen for requests
|
||||
-o, --output <OUTPUT>
|
||||
[default: text] [possible values: text, json]
|
||||
-h, --help
|
||||
Print help
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
```sh
|
||||
List all registered with gateways
|
||||
|
||||
Usage: nym-client list-gateways [OPTIONS] --id <ID>
|
||||
|
||||
Options:
|
||||
--id <ID> Id of client we want to list gateways for
|
||||
-o, --output <OUTPUT> [default: text] [possible values: text, json]
|
||||
-h, --help Print help
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
```sh
|
||||
Run the Nym client with provided configuration client optionally overriding set parameters
|
||||
|
||||
Usage: nym-client run [OPTIONS] --id <ID>
|
||||
|
||||
Options:
|
||||
--id <ID>
|
||||
Id of client we want to create config for
|
||||
--gateway <GATEWAY>
|
||||
Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened
|
||||
--nym-apis <NYM_APIS>
|
||||
Comma separated list of rest endpoints of the API validators
|
||||
--disable-socket <DISABLE_SOCKET>
|
||||
Whether to not start the websocket [possible values: true, false]
|
||||
-p, --port <PORT>
|
||||
Port for the socket to listen on
|
||||
--host <HOST>
|
||||
Ip for the socket (if applicable) to listen for requests
|
||||
-h, --help
|
||||
Print help
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
```sh
|
||||
Change the currently active gateway. Note that you must have already registered with the new gateway!
|
||||
|
||||
Usage: nym-client switch-gateway --id <ID> --gateway-id <GATEWAY_ID>
|
||||
|
||||
Options:
|
||||
--id <ID> Id of client we want to list gateways for
|
||||
--gateway-id <GATEWAY_ID> Id of the gateway we want to switch to
|
||||
-h, --help Print help
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
```sh
|
||||
Show bonding information of this node depending on its currently selected mode
|
||||
|
||||
Usage: nym-node bonding-information [OPTIONS]
|
||||
|
||||
Options:
|
||||
--id <ID> Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
|
||||
--config-file <CONFIG_FILE> Path to a configuration file of this node [env: NYMNODE_CONFIG=]
|
||||
--mode <MODE> [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway]
|
||||
-o, --output <OUTPUT> Specify the output format of the bonding information (`text` or `json`) [default: text] [possible values: text, json]
|
||||
-h, --help Print help
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user