diff --git a/.dockerignore b/.dockerignore index 09ed129139..a11022eb2b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,4 +4,3 @@ **/node_modules **/target dist -documentation diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index d7a76f2673..d07f7edcbd 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -30,10 +30,11 @@ jobs: strategy: fail-fast: false matrix: - os: [ arc-ubuntu-20.04, custom-runner-mac-m1 ] + os: [ arc-ubuntu-20.04, custom-windows-11, custom-runner-mac-m1 ] 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 diff --git a/.github/workflows/publish-nyms5-android-apk.yml b/.github/workflows/publish-nyms5-android-apk.yml index ab665ea660..b4518d57c3 100644 --- a/.github/workflows/publish-nyms5-android-apk.yml +++ b/.github/workflows/publish-nyms5-android-apk.yml @@ -56,14 +56,6 @@ jobs: rustup target add aarch64-linux-android \ x86_64-linux-android - - name: Build lib nym-socks5-listener - working-directory: sdk/lib/socks5-listener/ - env: - RELEASE: true - RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu" - # build for arm64 and x86_64 - run: ./build-android.sh aarch64 x86_64 - - name: Build APKs (unsigned) working-directory: nym-connect/native/android env: diff --git a/.github/workflows/push-credential-proxy.yaml b/.github/workflows/push-credential-proxy.yaml index ff3b02f728..6eb29e361c 100644 --- a/.github/workflows/push-credential-proxy.yaml +++ b/.github/workflows/push-credential-proxy.yaml @@ -26,7 +26,7 @@ jobs: git config --global user.name "Lawrence Stalder" - name: Get version from cargo.toml - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 id: get_version with: cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml diff --git a/.github/workflows/push-data-observatory.yaml b/.github/workflows/push-data-observatory.yaml index 8b5250995d..16026ee589 100644 --- a/.github/workflows/push-data-observatory.yaml +++ b/.github/workflows/push-data-observatory.yaml @@ -26,7 +26,7 @@ jobs: git config --global user.name "Lawrence Stalder" - name: Get version from cargo.toml - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 id: get_version with: cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml diff --git a/.github/workflows/push-network-monitor.yaml b/.github/workflows/push-network-monitor.yaml index 6856ec88d1..a63ae96be8 100644 --- a/.github/workflows/push-network-monitor.yaml +++ b/.github/workflows/push-network-monitor.yaml @@ -26,7 +26,7 @@ jobs: git config --global user.name "Lawrence Stalder" - name: Get version from cargo.toml - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 id: get_version with: cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml diff --git a/.github/workflows/push-node-status-agent.yaml b/.github/workflows/push-node-status-agent.yaml index 9f88f5c162..8400b7e7e1 100644 --- a/.github/workflows/push-node-status-agent.yaml +++ b/.github/workflows/push-node-status-agent.yaml @@ -8,7 +8,7 @@ on: description: Which gateway probe git ref to build the image with env: - WORKING_DIRECTORY: "nym-node-status-agent" + WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent" CONTAINER_NAME: "node-status-agent" jobs: @@ -31,7 +31,7 @@ jobs: git config --global user.name "Lawrence Stalder" - name: Get version from cargo.toml - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 id: get_version with: cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml @@ -58,4 +58,4 @@ jobs: - name: BuildAndPushImageOnHarbor run: | docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }} - docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags \ No newline at end of file + docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags diff --git a/.github/workflows/push-node-status-api.yaml b/.github/workflows/push-node-status-api.yaml index ed1bc660e3..bd19db3bc8 100644 --- a/.github/workflows/push-node-status-api.yaml +++ b/.github/workflows/push-node-status-api.yaml @@ -3,7 +3,7 @@ on: workflow_dispatch: env: - WORKING_DIRECTORY: "nym-node-status-api" + WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api" CONTAINER_NAME: "node-status-api" jobs: @@ -26,7 +26,7 @@ jobs: git config --global user.name "Lawrence Stalder" - name: Get version from cargo.toml - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 id: get_version with: cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml diff --git a/.github/workflows/push-nym-node.yaml b/.github/workflows/push-nym-node.yaml index 52dfeae0e7..79c25e022c 100644 --- a/.github/workflows/push-nym-node.yaml +++ b/.github/workflows/push-nym-node.yaml @@ -26,7 +26,7 @@ jobs: git config --global user.name "Lawrence Stalder" - name: Get version from cargo.toml - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 id: get_version with: cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml diff --git a/.github/workflows/push-nyx-chain-watcher.yaml b/.github/workflows/push-nyx-chain-watcher.yaml new file mode 100644 index 0000000000..084f4d11d7 --- /dev/null +++ b/.github/workflows/push-nyx-chain-watcher.yaml @@ -0,0 +1,55 @@ +name: Build and upload Nyx Chain Watcher container to harbor.nymte.ch +on: + workflow_dispatch: + +env: + WORKING_DIRECTORY: "nyx-chain-watcher" + CONTAINER_NAME: "nyx-chain-watcher" + +jobs: + build-container: + runs-on: arc-ubuntu-22.04-dind + steps: + - name: Login to Harbor + uses: docker/login-action@v3 + with: + registry: harbor.nymte.ch + username: ${{ secrets.HARBOR_ROBOT_USERNAME }} + password: ${{ secrets.HARBOR_ROBOT_SECRET }} + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Configure git identity + run: | + git config --global user.email "lawrence@nymtech.net" + git config --global user.name "Lawrence Stalder" + + - name: Get version from cargo.toml + uses: mikefarah/yq@v4.44.6 + id: get_version + with: + cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml + + - name: Check if tag exists + run: | + if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then + echo "Tag ${{ steps.get_version.outputs.value }} already exists" + fi + + - name: Remove existing tag if exists + run: | + if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then + git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + fi + + - name: Create tag + run: | + git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}" + git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + + - name: BuildAndPushImageOnHarbor + run: | + docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest + docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags diff --git a/.github/workflows/push-validator-rewarder.yaml b/.github/workflows/push-validator-rewarder.yaml index 8676520f82..db27ca4d64 100644 --- a/.github/workflows/push-validator-rewarder.yaml +++ b/.github/workflows/push-validator-rewarder.yaml @@ -26,10 +26,10 @@ jobs: git config --global user.name "Lawrence Stalder" - name: Get version from cargo.toml - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 id: get_version with: - cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml + cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml - name: Remove existing tag if exists run: | diff --git a/.gitignore b/.gitignore index 597794c0e3..817ddac4f2 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,6 @@ ppa-private-key.b64 ppa-private-key.asc nym-network-monitor/topology.json nym-network-monitor/__pycache__ -nym-network-monitor/*.key \ No newline at end of file +nym-network-monitor/*.key +nym-network-monitor/.envrc +nym-network-monitor/.envrc diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a79762d68..9c888a8eb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,86 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// ## [Unreleased] +## [2025.1-reeses] (2025-01-15) + +- Feture/legacy alert ([#5346]) +- chore: readjusted --mode behaviour to fix the regression ([#5331]) +- chore: apply 1.84 linter suggestions ([#5330]) +- bugfix: make sure refresh data key matches bond info ([#5329]) +- reduce log severity for number of packets being delayed ([#5321]) +- feat: warn users if node is run in exit mode only ([#5320]) +- Bugfix/contract version assignment ([#5318]) +- fixed client session histogram buckets ([#5316]) +- amend 250gb limit ([#5313]) +- feature: expand nym-node prometheus metrics ([#5298]) +- Cherry picked #5286 ([#5287]) +- Add close to credential storage ([#5283]) +- feature: wireguard metrics ([#5278]) +- Add PATCH support to nym-http-api-client ([#5260]) +- chore: removed legacy socks5 listener ([#5259]) +- bugfix: make sure to apply gateway score filtering when choosing initial node ([#5256]) +- Update TS bindings ([#5255]) +- Add conversion unit tests for auth msg ([#5251]) +- Add control messages to GatewayTransciver ([#5247]) +- Remove unneeded async function annotation ([#5246]) +- bugfix: make sure to update timestamp of last batch verification to prevent double redemption ([#5239]) +- Add FromStr impl for UserAgent ([#5236]) +- Extend swagger docs ([#5235]) +- TicketType derive Hash and Eq ([#5233]) +- Add fd callback to client core ([#5230]) +- Extend raw ws fd for gateway client ([#5218]) +- Shipping raw metrics to PG ([#5216]) +- Change sqlite journal mode to WAL ([#5213]) +- Derive serialize for UserAgent ([#5210]) +- Restore Location fields ([#5208]) +- better date serialization ([#5207]) +- Fix overflow ([#5204]) +- feature: hopefully final steps of the smoosh™️ ([#5201]) +- Fix overflow ([#5184]) +- NS API - Gateway stats scraping ([#5180]) +- introduced initial internal commands for nym-cli: ecash key and request generation ([#5174]) +- Move NS client to separate package under NS API ([#5171]) +- build(deps): bump micromatch from 4.0.4 to 4.0.8 in /testnet-faucet ([#4813]) + +[#5346]: https://github.com/nymtech/nym/pull/5346 +[#5331]: https://github.com/nymtech/nym/pull/5331 +[#5330]: https://github.com/nymtech/nym/pull/5330 +[#5329]: https://github.com/nymtech/nym/pull/5329 +[#5321]: https://github.com/nymtech/nym/pull/5321 +[#5320]: https://github.com/nymtech/nym/pull/5320 +[#5318]: https://github.com/nymtech/nym/pull/5318 +[#5316]: https://github.com/nymtech/nym/pull/5316 +[#5313]: https://github.com/nymtech/nym/pull/5313 +[#5298]: https://github.com/nymtech/nym/pull/5298 +[#5287]: https://github.com/nymtech/nym/pull/5287 +[#5283]: https://github.com/nymtech/nym/pull/5283 +[#5278]: https://github.com/nymtech/nym/pull/5278 +[#5260]: https://github.com/nymtech/nym/pull/5260 +[#5259]: https://github.com/nymtech/nym/pull/5259 +[#5256]: https://github.com/nymtech/nym/pull/5256 +[#5255]: https://github.com/nymtech/nym/pull/5255 +[#5251]: https://github.com/nymtech/nym/pull/5251 +[#5247]: https://github.com/nymtech/nym/pull/5247 +[#5246]: https://github.com/nymtech/nym/pull/5246 +[#5239]: https://github.com/nymtech/nym/pull/5239 +[#5236]: https://github.com/nymtech/nym/pull/5236 +[#5235]: https://github.com/nymtech/nym/pull/5235 +[#5233]: https://github.com/nymtech/nym/pull/5233 +[#5230]: https://github.com/nymtech/nym/pull/5230 +[#5218]: https://github.com/nymtech/nym/pull/5218 +[#5216]: https://github.com/nymtech/nym/pull/5216 +[#5213]: https://github.com/nymtech/nym/pull/5213 +[#5210]: https://github.com/nymtech/nym/pull/5210 +[#5208]: https://github.com/nymtech/nym/pull/5208 +[#5207]: https://github.com/nymtech/nym/pull/5207 +[#5204]: https://github.com/nymtech/nym/pull/5204 +[#5201]: https://github.com/nymtech/nym/pull/5201 +[#5184]: https://github.com/nymtech/nym/pull/5184 +[#5180]: https://github.com/nymtech/nym/pull/5180 +[#5174]: https://github.com/nymtech/nym/pull/5174 +[#5171]: https://github.com/nymtech/nym/pull/5171 +[#4813]: https://github.com/nymtech/nym/pull/4813 + ## [2024.14-crunch-patched] (2024-12-17) - Fixes an issue to allow previously registred clients to connect to latest nym-nodes diff --git a/Cargo.lock b/Cargo.lock index 9e09a9533e..b3197fd475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -140,23 +140,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" -[[package]] -name = "android_log-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" - -[[package]] -name = "android_logger" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826" -dependencies = [ - "android_log-sys", - "env_filter", - "log", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -283,7 +266,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -352,7 +335,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -363,7 +346,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -430,6 +413,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 +564,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -973,12 +964,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.0" @@ -1140,7 +1125,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1174,16 +1159,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - [[package]] name = "comfy-table" version = "7.1.1" @@ -1790,7 +1765,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1948,7 +1923,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1970,7 +1945,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core 0.20.9", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2021,7 +1996,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2064,7 +2039,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2097,7 +2072,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2165,7 +2140,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2354,6 +2329,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 +2416,7 @@ dependencies = [ [[package]] name = "explorer-api" -version = "1.1.43" +version = "1.1.44" dependencies = [ "chrono", "clap 4.5.20", @@ -2461,26 +2449,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "ext-trait" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d772df1c1a777963712fb68e014235e80863d6a91a85c4e06ba2d16243a310e5" -dependencies = [ - "ext-trait-proc_macros", -] - -[[package]] -name = "ext-trait-proc_macros" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab7934152eaf26aa5aa9f7371408ad5af4c31357073c9e84c3b9d7f11ad639a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "extension-storage" version = "1.3.0-rc.0" @@ -2497,21 +2465,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "extension-traits" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a296e5a895621edf9fa8329c83aa1cb69a964643e36cf54d8d7a69b789089537" -dependencies = [ - "ext-trait", -] - -[[package]] -name = "extern-c" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320bea982e85d42441eb25c49b41218e7eaa2657e8f90bc4eca7437376751e23" - [[package]] name = "eyre" version = "0.6.12" @@ -2523,15 +2476,21 @@ dependencies = [ ] [[package]] -name = "fancy_constructor" -version = "1.2.2" +name = "fallible-iterator" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fancy_constructor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b19d0e43eae2bfbafe4931b5e79c73fb1a849ca15cd41a761a7b8587f9a1a2" dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2765,7 +2724,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2860,7 +2819,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2887,15 +2846,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gloo-net" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" dependencies = [ "futures-channel", "futures-core", "futures-sink", "gloo-utils 0.2.0", - "http 0.2.12", + "http 1.1.0", "js-sys", "pin-project", "serde", @@ -3248,6 +3207,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" @@ -3467,7 +3432,8 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexed_db_futures" version = "0.4.2" -source = "git+https://github.com/TiemenSch/rust-indexed-db?branch=update-uuid#9745d015707008b0c410115d787014a6d1af2efb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0704b71f13f81b5933d791abf2de26b33c40935143985220299a357721166706" dependencies = [ "accessory", "cfg-if", @@ -3703,34 +3669,13 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -3822,9 +3767,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" @@ -3914,22 +3859,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "macro_rules_attribute" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf0c9b980bf4f3a37fd7b1c066941dd1b1d0152ce6ee6e8fe8c49b9f6810d862" -dependencies = [ - "macro_rules_attribute-proc_macro", - "paste", -] - -[[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58093314a45e00c77d5c508f76e77c3396afbbc0d01506e7fae47b018bac2b1d" - [[package]] name = "macroific" version = "1.3.1" @@ -3950,7 +3879,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3961,7 +3890,7 @@ checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3974,7 +3903,7 @@ dependencies = [ "macroific_core", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -4128,6 +4057,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 +4375,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -4451,7 +4395,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "nym-api" -version = "1.1.47" +version = "1.1.48" dependencies = [ "anyhow", "async-trait", @@ -4700,7 +4644,7 @@ dependencies = [ [[package]] name = "nym-cli" -version = "1.1.45" +version = "1.1.46" dependencies = [ "anyhow", "base64 0.22.1", @@ -4751,12 +4695,14 @@ dependencies = [ "nym-coconut-dkg-common", "nym-config", "nym-contracts-common", + "nym-credential-proxy-requests", "nym-credential-storage", "nym-credential-utils", "nym-credentials", "nym-credentials-interface", "nym-crypto", "nym-ecash-contract-common", + "nym-ecash-time", "nym-id", "nym-mixnet-contract-common", "nym-multisig-contract-common", @@ -4781,7 +4727,7 @@ dependencies = [ [[package]] name = "nym-client" -version = "1.1.45" +version = "1.1.46" dependencies = [ "bs58", "clap 4.5.20", @@ -4848,6 +4794,7 @@ dependencies = [ "nym-gateway-requests", "nym-id", "nym-metrics", + "nym-mixnet-client", "nym-network-defaults", "nym-nonexhaustive-delayqueue", "nym-pemstore", @@ -5006,16 +4953,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 +5388,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 +5480,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 +5495,6 @@ dependencies = [ name = "nym-gateway-storage" version = "0.1.0" dependencies = [ - "async-trait", "bincode", "defguard_wireguard_rs", "log", @@ -5742,20 +5681,22 @@ version = "0.1.0" dependencies = [ "dashmap", "lazy_static", - "log", "prometheus", + "tracing", ] [[package]] name = "nym-mixnet-client" version = "0.1.0" dependencies = [ + "dashmap", "futures", - "log", "nym-sphinx", "nym-task", "tokio", + "tokio-stream", "tokio-util", + "tracing", ] [[package]] @@ -5782,35 +5723,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" @@ -5819,11 +5731,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", @@ -5831,7 +5741,6 @@ dependencies = [ "nym-sphinx-params", "nym-sphinx-types", "nym-task", - "nym-validator-client", "rand", "serde", "thiserror", @@ -5881,7 +5790,9 @@ dependencies = [ "futures", "log", "nym-bin-common", + "nym-client-core", "nym-crypto", + "nym-gateway-requests", "nym-network-defaults", "nym-sdk", "nym-sphinx", @@ -5895,6 +5806,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "tokio-postgres", "tokio-util", "utoipa", "utoipa-swagger-ui", @@ -5902,7 +5814,7 @@ dependencies = [ [[package]] name = "nym-network-requester" -version = "1.1.46" +version = "1.1.47" dependencies = [ "addr", "anyhow", @@ -5953,9 +5865,12 @@ dependencies = [ [[package]] name = "nym-node" -version = "1.2.0" +version = "1.3.0" dependencies = [ "anyhow", + "async-trait", + "axum 0.7.7", + "axum-extra", "bip39", "bs58", "cargo_metadata 0.18.1", @@ -5963,6 +5878,10 @@ dependencies = [ "clap 4.5.20", "colored", "cupid", + "dashmap", + "futures", + "headers", + "human-repr", "humantime-serde", "ipnetwork 0.20.0", "nym-authenticator", @@ -5971,63 +5890,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]] @@ -6059,30 +5975,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.7" dependencies = [ "anyhow", "axum 0.7.7", - "bincode", "chrono", "clap 4.5.20", "cosmwasm-std", @@ -6090,11 +5999,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", @@ -6106,6 +6018,7 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "thiserror", + "time", "tokio", "tokio-util", "tower-http", @@ -6117,6 +6030,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" @@ -6309,7 +6237,7 @@ dependencies = [ [[package]] name = "nym-socks5-client" -version = "1.1.45" +version = "1.1.46" dependencies = [ "bs58", "clap 4.5.20", @@ -6372,28 +6300,6 @@ dependencies = [ "url", ] -[[package]] -name = "nym-socks5-listener" -version = "0.1.0" -dependencies = [ - "android_logger", - "anyhow", - "futures", - "jni", - "lazy_static", - "log", - "nym-bin-common", - "nym-client-core", - "nym-config", - "nym-credential-storage", - "nym-crypto", - "nym-socks5-client-core", - "rand", - "safer-ffi", - "serde", - "tokio", -] - [[package]] name = "nym-socks5-proxy-helpers" version = "0.1.0" @@ -6811,6 +6717,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,12 +6811,14 @@ dependencies = [ "nym-crypto", "nym-gateway-storage", "nym-network-defaults", + "nym-node-metrics", "nym-task", "nym-wireguard-types", "thiserror", "time", "tokio", "tokio-stream", + "tracing", "x25519-dalek", ] @@ -6912,7 +6839,7 @@ dependencies = [ [[package]] name = "nymvisor" -version = "0.1.10" +version = "0.1.11" dependencies = [ "anyhow", "bytes", @@ -7225,7 +7152,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7312,7 +7239,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7336,6 +7263,24 @@ dependencies = [ "indexmap 2.2.6", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher 0.3.11", +] + [[package]] name = "pin-project" version = "1.1.6" @@ -7353,7 +7298,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7474,6 +7419,35 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "postgres-protocol" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +dependencies = [ + "base64 0.22.1", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2 0.10.8", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -7502,20 +7476,10 @@ 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", ] -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -7568,14 +7532,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", ] @@ -7588,7 +7552,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "version_check", "yansi", ] @@ -7651,7 +7615,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7884,7 +7848,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8111,7 +8075,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.87", + "syn 2.0.90", "unicode-xid", "version_check", ] @@ -8237,7 +8201,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.87", + "syn 2.0.90", "walkdir", ] @@ -8293,9 +8257,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", @@ -8413,38 +8377,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "safer-ffi" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435fdd58b61a6f1d8545274c1dfa458e905ff68c166e65e294a0130ef5e675bd" -dependencies = [ - "extern-c", - "inventory", - "libc", - "macro_rules_attribute", - "paste", - "safer_ffi-proc_macros", - "scopeguard", - "stabby", - "uninit", - "unwind_safe", - "with_builtin_macros", -] - -[[package]] -name = "safer_ffi-proc_macros" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f25be5ba5f319542edb31925517e0380245ae37df50a9752cdbc05ef948156" -dependencies = [ - "macro_rules_attribute", - "prettyplease", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "same-file" version = "1.0.6" @@ -8486,7 +8418,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals 0.29.1", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8518,7 +8450,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8650,7 +8582,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8661,7 +8593,7 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8672,7 +8604,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8734,7 +8666,7 @@ checksum = "aafbefbe175fa9bf03ca83ef89beecff7d2a95aaacd5732325b90ac8c3bd7b90" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8755,7 +8687,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8806,7 +8738,7 @@ dependencies = [ "darling 0.20.9", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8877,12 +8809,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2-const-stable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" - [[package]] name = "sharded-slab" version = "0.1.7" @@ -9283,40 +9209,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "stabby" -version = "36.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311d6bcf0070c462ff626122ec2246f42bd2acd44b28908eedbfd07d500c7d99" -dependencies = [ - "rustversion", - "stabby-abi", -] - -[[package]] -name = "stabby-abi" -version = "36.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6daae1a0707399f56d27fce7f212e50e31d215112a447e1bbcd837ae1bf5f49" -dependencies = [ - "rustversion", - "sha2-const-stable", - "stabby-macros", -] - -[[package]] -name = "stabby-macros" -version = "36.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cf89a0cc9131279235baf8599b0e073fbcb096419204de0cc5d1a48ae73f74" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "rand", - "syn 1.0.109", -] - [[package]] name = "stable-pattern" version = "0.1.0" @@ -9405,7 +9297,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -9448,9 +9340,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", @@ -9756,7 +9648,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -9887,7 +9779,33 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", +] + +[[package]] +name = "tokio-postgres" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2", + "tokio", + "tokio-util", + "whoami", ] [[package]] @@ -10166,7 +10084,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10330,7 +10248,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "termcolor", ] @@ -10357,7 +10275,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals 0.28.0", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10560,7 +10478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10592,7 +10510,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.87", + "syn 2.0.90", "toml 0.5.11", "uniffi_build", "uniffi_meta", @@ -10635,15 +10553,6 @@ dependencies = [ "weedle2", ] -[[package]] -name = "uninit" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e130f2ed46ca5d8ec13c7ff95836827f92f5f5f37fd2b2bf16f33c408d98bb6" -dependencies = [ - "extension-traits", -] - [[package]] name = "universal-hash" version = "0.5.1" @@ -10666,12 +10575,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "unwind_safe" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0976c77def3f1f75c4ef892a292c31c0bbe9e3d0702c63044d7c76db298171a3" - [[package]] name = "url" version = "2.5.2" @@ -10724,7 +10627,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.87", + "syn 2.0.90", "uuid", ] @@ -10763,7 +10666,7 @@ checksum = "17e82ab96c5a55263b5bed151b8426410d93aa909a453acdbd4b6792b5af7d64" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10774,7 +10677,7 @@ checksum = "86b8338dc3c9526011ffaa2aa6bd60ddfda9d49d2123108690755c6e34844212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "utoipauto-core", ] @@ -10861,9 +10764,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -10872,36 +10775,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -10909,22 +10812,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-bindgen-test" @@ -10949,7 +10852,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -11032,9 +10935,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.2.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" +checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" dependencies = [ "futures", "js-sys", @@ -11046,9 +10949,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -11095,6 +10998,7 @@ checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ "redox_syscall 0.5.1", "wasite", + "web-sys", ] [[package]] @@ -11399,26 +11303,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "with_builtin_macros" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a59d55032495429b87f9d69954c6c8602e4d3f3e0a747a12dea6b0b23de685da" -dependencies = [ - "with_builtin_macros-proc_macros", -] - -[[package]] -name = "with_builtin_macros-proc_macros" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "wyz" version = "0.5.1" @@ -11477,7 +11361,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -11497,7 +11381,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7735cb8f59..fa53897399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,6 @@ members = [ "common/ip-packet-requests", "common/ledger", "common/mixnode-common", - "common/models", "common/network-defaults", "common/node-tester-utils", "common/nonexhaustive-delayqueue", @@ -99,17 +98,15 @@ members = [ "common/wasm/utils", "common/wireguard", "common/wireguard-types", - # "documentation/autodoc", + "documentation/autodoc", "explorer-api", "explorer-api/explorer-api-requests", "explorer-api/explorer-client", "gateway", "integrations/bity", - "mixnode", "sdk/ffi/cpp", "sdk/ffi/go", "sdk/ffi/shared", - "sdk/lib/socks5-listener", "sdk/rust/nym-sdk", "service-providers/authenticator", "service-providers/common", @@ -124,10 +121,11 @@ members = [ "nym-data-observatory", "nym-network-monitor", "nym-node", - "nym-node/nym-node-http-api", "nym-node/nym-node-requests", - "nym-node-status-api", - "nym-node-status-agent", + "nym-node/nym-node-metrics", + "nym-node-status-api/nym-node-status-agent", + "nym-node-status-api/nym-node-status-api", + "nym-node-status-api/nym-node-status-client", "nym-outfox", "nym-validator-rewarder", "tools/echo-server", @@ -149,23 +147,20 @@ members = [ "tools/internal/contract-state-importer/importer-cli", "tools/internal/contract-state-importer/importer-contract", "tools/internal/testnet-manager", - "tools/internal/testnet-manager/dkg-bypass-contract", + "tools/internal/testnet-manager/dkg-bypass-contract", "common/verloc", "tools/internal/mixnet-connectivity-check", ] default-members = [ "clients/native", "clients/socks5", - "common/models", "explorer-api", - "gateway", - "mixnode", "nym-api", "nym-credential-proxy/nym-credential-proxy", "nym-data-observatory", "nym-node", - "nym-node-status-api", + "nym-node-status-api/nym-node-status-agent", + "nym-node-status-api/nym-node-status-api", "nym-validator-rewarder", - "nym-node-status-api", "service-providers/authenticator", "service-providers/ip-packet-router", "service-providers/network-requester", @@ -264,6 +259,7 @@ http-body-util = "0.1" httpcodec = "0.2.3" humantime = "2.1.0" humantime-serde = "1.1.1" +human-repr = "1.1.0" hyper = "1.4.1" hyper-util = "0.1" indicatif = "0.17.8" @@ -398,19 +394,17 @@ prost = { version = "0.12", default-features = false } # wasm-related dependencies gloo-utils = "0.2.0" -gloo-net = "0.5.0" +gloo-net = "0.6.0" -# use a separate branch due to feature unification failures -# this is blocked until the upstream removes outdates `wasm_bindgen` feature usage -# indexed_db_futures = "0.4.1" -indexed_db_futures = { git = "https://github.com/TiemenSch/rust-indexed-db", branch = "update-uuid" } -js-sys = "0.3.70" +# TODO: migrate to 0.6+ +indexed_db_futures = "0.4.2" +js-sys = "0.3.76" serde-wasm-bindgen = "0.6.5" tsify = "0.4.5" -wasm-bindgen = "0.2.95" -wasm-bindgen-futures = "0.4.45" -wasmtimer = "0.2.0" -web-sys = "0.3.72" +wasm-bindgen = "0.2.99" +wasm-bindgen-futures = "0.4.49" +wasmtimer = "0.4.1" +web-sys = "0.3.76" # Profile settings for individual crates @@ -420,10 +414,6 @@ web-sys = "0.3.72" [profile.dev.package.sqlx-macros] opt-level = 3 -[profile.release.package.nym-socks5-listener] -strip = true -codegen-units = 1 - [profile.release.package.nym-client-wasm] # lto = true opt-level = 'z' diff --git a/README.md b/README.md index 8ebd3b590f..a29b215f0b 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr * `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications. * `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer. * `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework. +* `nym-cli` - a tool for interacting with the network from the CLI. @@ -35,24 +36,20 @@ client ───► Gateway ──┘ mix │ mix ┌─►mix ───► ### Building * Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html). -* Wallet build instructions are available on Nym [Technical docs](https://nymtech.net/docs/wallet/desktop-wallet.html). +* Wallet build instructions are available [here](https://github.com/nymtech/nym/tree/master/nym-wallet#installation-prerequisites---linux--mac). ### Developing -There's a [`sandbox.env`](https://github.com/nymtech/nym/envs/sandbox.env) file provided which you can rename to `.env` if you want convenient testing environment. Read more about sandbox environment in our [Operators Guide page](https://nymtech.net/operators/sandbox.html). - References for developers: -* [Developers Portal](https://nymtech.net/developers) -* [Typescript SDKs](https://sdk.nymtech.net/) -* [Technical Documentation - Nym network overview](https://nymtech.net/docs/) -* [Release Cycle - git flow](https://nymtech.net/operators/release-cycle.html) +* [Dev Docs](https://nymtech.net/docs/developers) +* [SDKs](https://nymtech.net/docs/developers/rust) +* [Network Docs](https://nymtech.net/docs/network) +* [Release Cycle - git flow](https://nymtech.net/docs/operators/release-cycle) ### Developer chat -You can chat to us in two places: -* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) -* The various developer channels on [Discord](https://nymtech.net/go/discord) +You can chat to us in the #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) or on the [Nym Forum](https://forum.nymtech.net). ### Tokenomics & Rewards diff --git a/SECURITY.md b/SECURITY.md index c3b9bb9748..23091d1cbc 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -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 -sub rsa4096 2023-10-30 [E] [expire : 2026-10-29] +uid [ ultime ] Security Nym Technologies +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----- + ``` diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 03eb55186e..5f95bbb088 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-client" -version = "1.1.45" +version = "1.1.46" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] description = "Implementation of the Nym Client" edition = "2021" diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 4363c56387..d538cd5b75 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-socks5-client" -version = "1.1.45" +version = "1.1.46" authors = ["Dave Hrycyszyn "] description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" edition = "2021" diff --git a/common/authenticator-requests/src/lib.rs b/common/authenticator-requests/src/lib.rs index 27164417e8..ed987a9a50 100644 --- a/common/authenticator-requests/src/lib.rs +++ b/common/authenticator-requests/src/lib.rs @@ -8,6 +8,7 @@ pub mod v3; pub mod v4; mod error; +mod util; pub use error::Error; pub use v4 as latest; diff --git a/common/authenticator-requests/src/util.rs b/common/authenticator-requests/src/util.rs new file mode 100644 index 0000000000..cb1269f10c --- /dev/null +++ b/common/authenticator-requests/src/util.rs @@ -0,0 +1,71 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(test)] +pub(crate) mod tests { + pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [ + 0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254, + 16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139, + 154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123, + 35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1, + 151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43, + 90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193, + 39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81, + 184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195, + 196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48, + 92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48, + 166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186, + 19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38, + 228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85, + 88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241, + 85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112, + 48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31, + 97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203, + 71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107, + 213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180, + 178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1, + 96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166, + 30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212, + 207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71, + 223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22, + 119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131, + 59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119, + 240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202, + 58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144, + 189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33, + 30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150, + 74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6, + 227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85, + 15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38, + 161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20, + 47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48, + 135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170, + 150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109, + 55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249, + 87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144, + 178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32, + 203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184, + 96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169, + 24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61, + 130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82, + 108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1, + 32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234, + 150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241, + 203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217, + 160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52, + 147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81, + 70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233, + 178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98, + 110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69, + 131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251, + 197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233, + 162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106, + 83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145, + 192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124, + 55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0, + 0, 0, 0, 0, 0, 1, + ]; + pub(crate) const RECIPIENT: &str = "CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f"; +} diff --git a/common/authenticator-requests/src/v2/registration.rs b/common/authenticator-requests/src/v2/registration.rs index b7bf2d8430..f3aa22d749 100644 --- a/common/authenticator-requests/src/v2/registration.rs +++ b/common/authenticator-requests/src/v2/registration.rs @@ -29,7 +29,7 @@ pub type Taken = Option; pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct InitMessage { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, @@ -41,7 +41,7 @@ impl InitMessage { } } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct FinalMessage { /// Gateway client data pub gateway_client: GatewayClient, @@ -50,28 +50,28 @@ pub struct FinalMessage { pub credential: Option, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RegistrationData { pub nonce: u64, pub gateway_data: GatewayClient, pub wg_port: u16, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RegistredData { pub pub_key: PeerPublicKey, pub private_ip: IpAddr, pub wg_port: u16, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RemainingBandwidthData { pub available_bandwidth: i64, } /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Gateway/Nym node can then verify pub_key payload using the same process -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct GatewayClient { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, @@ -147,7 +147,7 @@ impl GatewayClient { // TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO2: rely on our internal crypto/hmac -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ClientMac(Vec); impl fmt::Display for ClientMac { diff --git a/common/authenticator-requests/src/v2/request.rs b/common/authenticator-requests/src/v2/request.rs index 6943085af7..abd1e5ebff 100644 --- a/common/authenticator-requests/src/v2/request.rs +++ b/common/authenticator-requests/src/v2/request.rs @@ -87,7 +87,7 @@ impl AuthenticatorRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum AuthenticatorRequestData { Initial(InitMessage), Final(Box), diff --git a/common/authenticator-requests/src/v2/response.rs b/common/authenticator-requests/src/v2/response.rs index ab05dfcd35..1b389de43f 100644 --- a/common/authenticator-requests/src/v2/response.rs +++ b/common/authenticator-requests/src/v2/response.rs @@ -100,28 +100,28 @@ impl AuthenticatorResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum AuthenticatorResponseData { PendingRegistration(PendingRegistrationResponse), Registered(RegisteredResponse), RemainingBandwidth(RemainingBandwidthResponse), } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct PendingRegistrationResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: RegistrationData, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RegisteredResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: RegistredData, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RemainingBandwidthResponse { pub request_id: u64, pub reply_to: Recipient, diff --git a/common/authenticator-requests/src/v3/conversion.rs b/common/authenticator-requests/src/v3/conversion.rs index fe0699ab8b..1a55576a0d 100644 --- a/common/authenticator-requests/src/v3/conversion.rs +++ b/common/authenticator-requests/src/v3/conversion.rs @@ -19,6 +19,24 @@ impl From for v3::request::AuthenticatorReque } } +impl TryFrom for v2::request::AuthenticatorRequest { + type Error = crate::Error; + + fn try_from( + authenticator_request: v3::request::AuthenticatorRequest, + ) -> Result { + Ok(Self { + protocol: Protocol { + version: 2, + service_provider_type: ServiceProviderType::Authenticator, + }, + data: authenticator_request.data.try_into()?, + reply_to: authenticator_request.reply_to, + request_id: authenticator_request.request_id, + }) + } +} + impl From for v3::request::AuthenticatorRequestData { fn from(authenticator_request_data: v2::request::AuthenticatorRequestData) -> Self { match authenticator_request_data { @@ -35,6 +53,29 @@ impl From for v3::request::AuthenticatorR } } +impl TryFrom for v2::request::AuthenticatorRequestData { + type Error = crate::Error; + + fn try_from( + authenticator_request_data: v3::request::AuthenticatorRequestData, + ) -> Result { + match authenticator_request_data { + v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok( + v2::request::AuthenticatorRequestData::Initial(init_msg.into()), + ), + v3::request::AuthenticatorRequestData::Final(gw_client) => Ok( + v2::request::AuthenticatorRequestData::Final(gw_client.into()), + ), + v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok( + v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key), + ), + v3::request::AuthenticatorRequestData::TopUpBandwidth(_) => Err( + Self::Error::Conversion("no top up bandwidth variant in v2".to_string()), + ), + } + } +} + impl From for v3::registration::InitMessage { fn from(init_msg: v2::registration::InitMessage) -> Self { Self { @@ -43,6 +84,14 @@ impl From for v3::registration::InitMessage { } } +impl From for v2::registration::InitMessage { + fn from(init_msg: v3::registration::InitMessage) -> Self { + Self { + pub_key: init_msg.pub_key, + } + } +} + impl From> for Box { fn from(gw_client: Box) -> Self { Box::new(v3::registration::FinalMessage { @@ -52,6 +101,15 @@ impl From> for Box> for Box { + fn from(gw_client: Box) -> Self { + Box::new(v2::registration::FinalMessage { + gateway_client: gw_client.gateway_client.into(), + credential: gw_client.credential, + }) + } +} + impl From for v3::registration::GatewayClient { fn from(gw_client: v2::registration::GatewayClient) -> Self { Self { @@ -93,7 +151,10 @@ impl TryFrom for v2::response::Authenticato Ok(Self { data: authenticator_response.data.try_into()?, reply_to: authenticator_response.reply_to, - protocol: authenticator_response.protocol, + protocol: Protocol { + version: 2, + service_provider_type: authenticator_response.protocol.service_provider_type, + }, }) } } @@ -101,7 +162,10 @@ impl TryFrom for v2::response::Authenticato impl From for v3::response::AuthenticatorResponse { fn from(value: v2::response::AuthenticatorResponse) -> Self { Self { - protocol: value.protocol, + protocol: Protocol { + version: 3, + service_provider_type: value.protocol.service_provider_type, + }, data: value.data.into(), reply_to: value.reply_to, } @@ -270,3 +334,511 @@ impl From for v3::registration::Remain } } } + +#[cfg(test)] +mod tests { + use std::{net::IpAddr, str::FromStr}; + + use nym_credentials_interface::CredentialSpendingData; + use nym_crypto::asymmetric::encryption::PrivateKey; + use nym_sphinx::addressing::Recipient; + use nym_wireguard_types::PeerPublicKey; + use x25519_dalek::PublicKey; + + use super::*; + use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT}; + + #[test] + fn upgrade_initial_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v2::request::AuthenticatorRequest::new_initial_request( + v2::registration::InitMessage::new(pub_key), + reply_to, + ); + let upgraded_msg = v3::request::AuthenticatorRequest::from(msg); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage { + pub_key + }) + ); + } + + #[test] + fn downgrade_initial_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request( + v3::registration::InitMessage::new(pub_key), + reply_to, + ); + let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 2, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v2::request::AuthenticatorRequestData::Initial(v2::registration::InitMessage { + pub_key + }) + ); + } + + #[test] + fn upgrade_final_req() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let private_ip = IpAddr::from_str("10.10.10.10").unwrap(); + let nonce = 42; + let gateway_client = v2::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ); + let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap()); + let final_message = v2::registration::FinalMessage { + gateway_client, + credential: credential.clone(), + }; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = + v2::request::AuthenticatorRequest::new_final_request(final_message, reply_to); + let upgraded_msg = v3::request::AuthenticatorRequest::from(msg); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v3::request::AuthenticatorRequestData::Final(Box::new( + v3::registration::FinalMessage { + gateway_client: v3::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ), + credential + } + )) + ); + } + + #[test] + fn downgrade_final_req() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let private_ip = IpAddr::from_str("10.10.10.10").unwrap(); + let nonce = 42; + let gateway_client = v3::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ); + let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap()); + let final_message = v3::registration::FinalMessage { + gateway_client, + credential: credential.clone(), + }; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = + v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to); + let upgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 2, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v2::request::AuthenticatorRequestData::Final(Box::new( + v2::registration::FinalMessage { + gateway_client: v2::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ), + credential + } + )) + ); + } + + #[test] + fn upgrade_query_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v2::request::AuthenticatorRequest::new_query_request(pub_key, reply_to); + let upgraded_msg = v3::request::AuthenticatorRequest::from(msg); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) + ); + } + + #[test] + fn downgrade_query_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to); + let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 2, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key) + ); + } + + #[test] + fn downgrade_topup_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(); + let top_up_message = v3::topup::TopUpMessage { + pub_key, + credential, + }; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = + v3::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to); + assert!(v2::request::AuthenticatorRequest::try_from(msg).is_err()); + } + + #[test] + fn upgrade_pending_reg_resp() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let private_ip = IpAddr::from_str("10.10.10.10").unwrap(); + let nonce = 42; + let wg_port = 51822; + let gateway_data = v2::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ); + let registration_data = v2::registration::RegistrationData { + nonce, + gateway_data, + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v2::response::AuthenticatorResponse::new_pending_registration_success( + registration_data, + request_id, + reply_to, + ); + let upgraded_msg = v3::response::AuthenticatorResponse::from(msg); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v3::response::AuthenticatorResponseData::PendingRegistration( + v3::response::PendingRegistrationResponse { + request_id, + reply_to, + reply: v3::registration::RegistrationData { + nonce, + gateway_data: v3::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ), + wg_port, + } + } + ) + ); + } + + #[test] + fn downgrade_pending_reg_resp() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let private_ip = IpAddr::from_str("10.10.10.10").unwrap(); + let nonce = 42; + let wg_port = 51822; + let gateway_data = v3::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ); + let registration_data = v3::registration::RegistrationData { + nonce, + gateway_data, + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v3::response::AuthenticatorResponse::new_pending_registration_success( + registration_data, + request_id, + reply_to, + ); + let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 2, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v2::response::AuthenticatorResponseData::PendingRegistration( + v2::response::PendingRegistrationResponse { + request_id, + reply_to, + reply: v2::registration::RegistrationData { + nonce, + gateway_data: v2::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ip, + nonce, + ), + wg_port, + } + } + ) + ); + } + + #[test] + fn upgrade_registered_resp() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let private_ip = IpAddr::from_str("10.10.10.10").unwrap(); + let wg_port = 51822; + let registred_data = v2::registration::RegistredData { + pub_key, + private_ip, + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v2::response::AuthenticatorResponse::new_registered( + registred_data, + reply_to, + request_id, + ); + let upgraded_msg = v3::response::AuthenticatorResponse::from(msg); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse { + request_id, + reply_to, + reply: v3::registration::RegistredData { + wg_port, + pub_key, + private_ip + } + }) + ); + } + + #[test] + fn downgrade_registered_resp() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let private_ip = IpAddr::from_str("10.10.10.10").unwrap(); + let wg_port = 51822; + let registred_data = v3::registration::RegistredData { + pub_key, + private_ip, + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v3::response::AuthenticatorResponse::new_registered( + registred_data, + reply_to, + request_id, + ); + let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 2, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse { + request_id, + reply_to, + reply: v2::registration::RegistredData { + wg_port, + pub_key, + private_ip + } + }) + ); + } + + #[test] + fn upgrade_remaining_bandwidth_resp() { + let available_bandwidth = 42; + let remaining_bandwidth_data = Some(v2::registration::RemainingBandwidthData { + available_bandwidth, + }); + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v2::response::AuthenticatorResponse::new_remaining_bandwidth( + remaining_bandwidth_data, + reply_to, + request_id, + ); + let upgraded_msg = v3::response::AuthenticatorResponse::from(msg); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v3::response::AuthenticatorResponseData::RemainingBandwidth( + v3::response::RemainingBandwidthResponse { + request_id, + reply_to, + reply: Some(v3::registration::RemainingBandwidthData { + available_bandwidth, + }) + } + ) + ); + } + + #[test] + fn downgrade_remaining_bandwidth_resp() { + let available_bandwidth = 42; + let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData { + available_bandwidth, + }); + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth( + remaining_bandwidth_data, + reply_to, + request_id, + ); + let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 2, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v2::response::AuthenticatorResponseData::RemainingBandwidth( + v2::response::RemainingBandwidthResponse { + request_id, + reply_to, + reply: Some(v2::registration::RemainingBandwidthData { + available_bandwidth, + }) + } + ) + ); + } + + #[test] + fn downgrade_topup_resp() { + let available_bandwidth = 42; + let remaining_bandwidth_data = v3::registration::RemainingBandwidthData { + available_bandwidth, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v3::response::AuthenticatorResponse::new_topup_bandwidth( + remaining_bandwidth_data, + reply_to, + request_id, + ); + assert!(v2::response::AuthenticatorResponse::try_from(msg).is_err()); + } +} diff --git a/common/authenticator-requests/src/v3/registration.rs b/common/authenticator-requests/src/v3/registration.rs index 37234f7e1f..d9fac785a8 100644 --- a/common/authenticator-requests/src/v3/registration.rs +++ b/common/authenticator-requests/src/v3/registration.rs @@ -29,7 +29,7 @@ pub type Taken = Option; pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct InitMessage { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, @@ -41,7 +41,7 @@ impl InitMessage { } } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct FinalMessage { /// Gateway client data pub gateway_client: GatewayClient, @@ -50,28 +50,28 @@ pub struct FinalMessage { pub credential: Option, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RegistrationData { pub nonce: u64, pub gateway_data: GatewayClient, pub wg_port: u16, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RegistredData { pub pub_key: PeerPublicKey, pub private_ip: IpAddr, pub wg_port: u16, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RemainingBandwidthData { pub available_bandwidth: i64, } /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Gateway/Nym node can then verify pub_key payload using the same process -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct GatewayClient { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, @@ -147,7 +147,7 @@ impl GatewayClient { // TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO2: rely on our internal crypto/hmac -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ClientMac(Vec); impl fmt::Display for ClientMac { diff --git a/common/authenticator-requests/src/v3/request.rs b/common/authenticator-requests/src/v3/request.rs index 32db17aed2..9a7940e1cc 100644 --- a/common/authenticator-requests/src/v3/request.rs +++ b/common/authenticator-requests/src/v3/request.rs @@ -106,7 +106,7 @@ impl AuthenticatorRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum AuthenticatorRequestData { Initial(InitMessage), Final(Box), diff --git a/common/authenticator-requests/src/v3/response.rs b/common/authenticator-requests/src/v3/response.rs index 370fc64671..ca44fb19f6 100644 --- a/common/authenticator-requests/src/v3/response.rs +++ b/common/authenticator-requests/src/v3/response.rs @@ -120,7 +120,7 @@ impl AuthenticatorResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum AuthenticatorResponseData { PendingRegistration(PendingRegistrationResponse), Registered(RegisteredResponse), @@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData { TopUpBandwidth(TopUpBandwidthResponse), } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct PendingRegistrationResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: RegistrationData, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RegisteredResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: RegistredData, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RemainingBandwidthResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: Option, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct TopUpBandwidthResponse { pub request_id: u64, pub reply_to: Recipient, diff --git a/common/authenticator-requests/src/v3/topup.rs b/common/authenticator-requests/src/v3/topup.rs index 31a61a0659..1163d07f12 100644 --- a/common/authenticator-requests/src/v3/topup.rs +++ b/common/authenticator-requests/src/v3/topup.rs @@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData; use nym_wireguard_types::PeerPublicKey; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct TopUpMessage { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, diff --git a/common/authenticator-requests/src/v4/conversion.rs b/common/authenticator-requests/src/v4/conversion.rs index 7b79b5f087..86a1c8791b 100644 --- a/common/authenticator-requests/src/v4/conversion.rs +++ b/common/authenticator-requests/src/v4/conversion.rs @@ -3,37 +3,82 @@ use nym_service_provider_requests_common::{Protocol, ServiceProviderType}; -use crate::{v2, v3, v4}; +use crate::{v3, v4}; -impl From for v4::request::AuthenticatorRequest { - fn from(authenticator_request: v3::request::AuthenticatorRequest) -> Self { - Self { +impl TryFrom for v4::request::AuthenticatorRequest { + type Error = crate::Error; + fn try_from( + authenticator_request: v3::request::AuthenticatorRequest, + ) -> Result { + Ok(Self { protocol: Protocol { version: 4, service_provider_type: ServiceProviderType::Authenticator, }, - data: authenticator_request.data.into(), + data: authenticator_request.data.try_into()?, reply_to: authenticator_request.reply_to, request_id: authenticator_request.request_id, + }) + } +} + +impl TryFrom for v3::request::AuthenticatorRequest { + type Error = crate::Error; + fn try_from( + authenticator_request: v4::request::AuthenticatorRequest, + ) -> Result { + Ok(Self { + protocol: Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator, + }, + data: authenticator_request.data.try_into()?, + reply_to: authenticator_request.reply_to, + request_id: authenticator_request.request_id, + }) + } +} + +impl TryFrom for v4::request::AuthenticatorRequestData { + type Error = crate::Error; + fn try_from( + authenticator_request_data: v3::request::AuthenticatorRequestData, + ) -> Result { + match authenticator_request_data { + v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok( + v4::request::AuthenticatorRequestData::Initial(init_msg.into()), + ), + v3::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion( + "mac hash breaking change".to_string(), + )), + v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok( + v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key), + ), + v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok( + v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()), + ), } } } -impl From for v4::request::AuthenticatorRequestData { - fn from(authenticator_request_data: v3::request::AuthenticatorRequestData) -> Self { +impl TryFrom for v3::request::AuthenticatorRequestData { + type Error = crate::Error; + fn try_from( + authenticator_request_data: v4::request::AuthenticatorRequestData, + ) -> Result { match authenticator_request_data { - v3::request::AuthenticatorRequestData::Initial(init_msg) => { - v4::request::AuthenticatorRequestData::Initial(init_msg.into()) - } - v3::request::AuthenticatorRequestData::Final(gw_client) => { - v4::request::AuthenticatorRequestData::Final(gw_client.into()) - } - v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => { - v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) - } - v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => { - v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()) - } + v4::request::AuthenticatorRequestData::Initial(init_msg) => Ok( + v3::request::AuthenticatorRequestData::Initial(init_msg.into()), + ), + v4::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion( + "mac hash breaking change".to_string(), + )), + v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok( + v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key), + ), + v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok( + v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()), + ), } } } @@ -46,12 +91,11 @@ impl From for v4::registration::InitMessage { } } -impl From> for Box { - fn from(gw_client: Box) -> Self { - Box::new(v4::registration::FinalMessage { - gateway_client: gw_client.gateway_client.into(), - credential: gw_client.credential, - }) +impl From for v3::registration::InitMessage { + fn from(init_msg: v4::registration::InitMessage) -> Self { + Self { + pub_key: init_msg.pub_key, + } } } @@ -64,67 +108,26 @@ impl From> for Box { } } -impl From for v4::registration::GatewayClient { - fn from(gw_client: v2::registration::GatewayClient) -> Self { - Self { - pub_key: gw_client.pub_key, - private_ips: gw_client.private_ip.into(), - mac: gw_client.mac.into(), - } +impl From> for Box { + fn from(top_up_message: Box) -> Self { + Box::new(v3::topup::TopUpMessage { + pub_key: top_up_message.pub_key, + credential: top_up_message.credential, + }) } } -impl From for v4::registration::GatewayClient { - fn from(gw_client: v3::registration::GatewayClient) -> Self { - Self { - pub_key: gw_client.pub_key, - private_ips: gw_client.private_ip.into(), - mac: gw_client.mac.into(), - } - } -} - -impl From for v3::registration::GatewayClient { - fn from(gw_client: v4::registration::GatewayClient) -> Self { - Self { - pub_key: gw_client.pub_key, - private_ip: gw_client.private_ips.ipv4.into(), - mac: gw_client.mac.into(), - } - } -} - -impl From for v2::registration::GatewayClient { - fn from(gw_client: v4::registration::GatewayClient) -> Self { - Self { - pub_key: gw_client.pub_key, - private_ip: gw_client.private_ips.ipv4.into(), - mac: gw_client.mac.into(), - } - } -} - -impl From for v4::registration::ClientMac { - fn from(mac: v2::registration::ClientMac) -> Self { - Self::new(mac.to_vec()) - } -} - -impl From for v4::registration::ClientMac { - fn from(mac: v3::registration::ClientMac) -> Self { - Self::new(mac.to_vec()) - } -} - -impl From for v3::registration::ClientMac { - fn from(mac: v4::registration::ClientMac) -> Self { - Self::new(mac.to_vec()) - } -} - -impl From for v2::registration::ClientMac { - fn from(mac: v4::registration::ClientMac) -> Self { - Self::new(mac.to_vec()) +impl TryFrom for v4::response::AuthenticatorResponse { + type Error = crate::Error; + fn try_from(value: v3::response::AuthenticatorResponse) -> Result { + Ok(Self { + protocol: Protocol { + version: 4, + service_provider_type: value.protocol.service_provider_type, + }, + data: value.data.try_into()?, + reply_to: value.reply_to, + }) } } @@ -137,11 +140,40 @@ impl TryFrom for v3::response::Authenticato Ok(Self { data: authenticator_response.data.try_into()?, reply_to: authenticator_response.reply_to, - protocol: authenticator_response.protocol, + protocol: Protocol { + version: 3, + service_provider_type: authenticator_response.protocol.service_provider_type, + }, }) } } +impl TryFrom for v4::response::AuthenticatorResponseData { + type Error = crate::Error; + fn try_from( + authenticator_response_data: v3::response::AuthenticatorResponseData, + ) -> Result { + match authenticator_response_data { + v3::response::AuthenticatorResponseData::PendingRegistration(_) => Err( + Self::Error::Conversion("mac hash breaking change".to_string()), + ), + + v3::response::AuthenticatorResponseData::Registered(registered_response) => Ok( + v4::response::AuthenticatorResponseData::Registered(registered_response.into()), + ), + + v3::response::AuthenticatorResponseData::RemainingBandwidth( + remaining_bandwidth_response, + ) => Ok(v4::response::AuthenticatorResponseData::RemainingBandwidth( + remaining_bandwidth_response.into(), + )), + v3::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => Ok( + v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into()), + ), + } + } +} + impl TryFrom for v3::response::AuthenticatorResponseData { type Error = crate::Error; @@ -149,13 +181,10 @@ impl TryFrom for v3::response::Authenti authenticator_response_data: v4::response::AuthenticatorResponseData, ) -> Result { match authenticator_response_data { - v4::response::AuthenticatorResponseData::PendingRegistration( - pending_registration_response, - ) => Ok( - v3::response::AuthenticatorResponseData::PendingRegistration( - pending_registration_response.into(), - ), + v4::response::AuthenticatorResponseData::PendingRegistration(_) => Err( + Self::Error::Conversion("mac hash breaking change".to_string()), ), + v4::response::AuthenticatorResponseData::Registered(registered_response) => Ok( v3::response::AuthenticatorResponseData::Registered(registered_response.into()), ), @@ -173,8 +202,8 @@ impl TryFrom for v3::response::Authenti } } -impl From for v3::response::PendingRegistrationResponse { - fn from(value: v4::response::PendingRegistrationResponse) -> Self { +impl From for v3::response::RegisteredResponse { + fn from(value: v4::response::RegisteredResponse) -> Self { Self { request_id: value.request_id, reply_to: value.reply_to, @@ -183,8 +212,8 @@ impl From for v3::response::PendingRe } } -impl From for v3::response::RegisteredResponse { - fn from(value: v4::response::RegisteredResponse) -> Self { +impl From for v4::response::RegisteredResponse { + fn from(value: v3::response::RegisteredResponse) -> Self { Self { request_id: value.request_id, reply_to: value.reply_to, @@ -193,6 +222,16 @@ impl From for v3::response::RegisteredResponse } } +impl From for v4::response::RemainingBandwidthResponse { + fn from(value: v3::response::RemainingBandwidthResponse) -> Self { + Self { + request_id: value.request_id, + reply_to: value.reply_to, + reply: value.reply.map(Into::into), + } + } +} + impl From for v3::response::RemainingBandwidthResponse { fn from(value: v4::response::RemainingBandwidthResponse) -> Self { Self { @@ -203,11 +242,31 @@ impl From for v3::response::RemainingB } } -impl From for v3::registration::RegistrationData { - fn from(value: v4::registration::RegistrationData) -> Self { +impl From for v4::response::TopUpBandwidthResponse { + fn from(value: v3::response::TopUpBandwidthResponse) -> Self { Self { - nonce: value.nonce, - gateway_data: value.gateway_data.into(), + request_id: value.request_id, + reply_to: value.reply_to, + reply: value.reply.into(), + } + } +} + +impl From for v3::response::TopUpBandwidthResponse { + fn from(value: v4::response::TopUpBandwidthResponse) -> Self { + Self { + request_id: value.request_id, + reply_to: value.reply_to, + reply: value.reply.into(), + } + } +} + +impl From for v4::registration::RegistredData { + fn from(value: v3::registration::RegistredData) -> Self { + Self { + pub_key: value.pub_key, + private_ips: value.private_ip.into(), wg_port: value.wg_port, } } @@ -223,6 +282,14 @@ impl From for v3::registration::RegistredData { } } +impl From for v4::registration::RemainingBandwidthData { + fn from(value: v3::registration::RemainingBandwidthData) -> Self { + Self { + available_bandwidth: value.available_bandwidth, + } + } +} + impl From for v3::registration::RemainingBandwidthData { fn from(value: v4::registration::RemainingBandwidthData) -> Self { Self { @@ -230,3 +297,441 @@ impl From for v3::registration::Remain } } } + +#[cfg(test)] +mod tests { + use std::{ + net::{Ipv4Addr, Ipv6Addr}, + str::FromStr, + }; + + use nym_credentials_interface::CredentialSpendingData; + use nym_crypto::asymmetric::encryption::PrivateKey; + use nym_sphinx::addressing::Recipient; + use nym_wireguard_types::PeerPublicKey; + use x25519_dalek::PublicKey; + + use super::*; + use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT}; + + #[test] + fn upgrade_initial_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request( + v3::registration::InitMessage::new(pub_key), + reply_to, + ); + let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 4, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v4::request::AuthenticatorRequestData::Initial(v4::registration::InitMessage { + pub_key + }) + ); + } + + #[test] + fn downgrade_initial_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v4::request::AuthenticatorRequest::new_initial_request( + v4::registration::InitMessage::new(pub_key), + reply_to, + ); + let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage { + pub_key + }) + ); + } + + #[test] + fn upgrade_final_req() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap(); + let nonce = 42; + let gateway_client = v3::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + ipv4.into(), + nonce, + ); + let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap()); + let final_message = v3::registration::FinalMessage { + gateway_client, + credential: credential.clone(), + }; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = + v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to); + assert!(v4::request::AuthenticatorRequest::try_from(msg).is_err()); + } + + #[test] + fn downgrade_final_req() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap(); + let private_ips = + v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap()); + let nonce = 42; + let gateway_client = v4::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ips, + nonce, + ); + let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap()); + let final_message = v4::registration::FinalMessage { + gateway_client, + credential: credential.clone(), + }; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = + v4::request::AuthenticatorRequest::new_final_request(final_message, reply_to); + assert!(v3::request::AuthenticatorRequest::try_from(msg).is_err()); + } + + #[test] + fn upgrade_query_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to); + let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 4, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) + ); + } + + #[test] + fn downgrade_query_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = v4::request::AuthenticatorRequest::new_query_request(pub_key, reply_to); + let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) + ); + } + + #[test] + fn downgrade_topup_req() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(); + let top_up_message = v4::topup::TopUpMessage { + pub_key, + credential: credential.clone(), + }; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let (msg, _) = + v4::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to); + let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v3::request::AuthenticatorRequestData::TopUpBandwidth(Box::new( + v3::topup::TopUpMessage { + pub_key, + credential + } + )) + ); + } + + #[test] + fn upgrade_pending_reg_resp() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap(); + let nonce = 42; + let wg_port = 51822; + let gateway_data = v3::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + ipv4.into(), + nonce, + ); + let registration_data = v3::registration::RegistrationData { + nonce, + gateway_data, + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v3::response::AuthenticatorResponse::new_pending_registration_success( + registration_data, + request_id, + reply_to, + ); + assert!(v4::response::AuthenticatorResponse::try_from(msg).is_err()); + } + + #[test] + fn downgrade_pending_reg_resp() { + let mut rng = rand::thread_rng(); + + let local_secret = PrivateKey::new(&mut rng); + let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng); + let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap(); + let private_ips = + v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap()); + let nonce = 42; + let wg_port = 51822; + let gateway_data = v4::registration::GatewayClient::new( + &local_secret, + (&remote_secret).into(), + private_ips, + nonce, + ); + let registration_data = v4::registration::RegistrationData { + nonce, + gateway_data, + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v4::response::AuthenticatorResponse::new_pending_registration_success( + registration_data, + request_id, + reply_to, + ); + assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err()); + } + + #[test] + fn upgrade_registered_resp() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap(); + let private_ips = + v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap()); + let wg_port = 51822; + let registred_data = v3::registration::RegistredData { + pub_key, + private_ip: ipv4.into(), + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v3::response::AuthenticatorResponse::new_registered( + registred_data, + reply_to, + request_id, + ); + let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap(); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 4, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse { + request_id, + reply_to, + reply: v4::registration::RegistredData { + wg_port, + pub_key, + private_ips + } + }) + ); + } + + #[test] + fn downgrade_registered_resp() { + let pub_key = PeerPublicKey::new(PublicKey::from([0; 32])); + let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap(); + let private_ips = + v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap()); + let wg_port = 51822; + let registred_data = v4::registration::RegistredData { + pub_key, + private_ips, + wg_port, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v4::response::AuthenticatorResponse::new_registered( + registred_data, + reply_to, + request_id, + ); + let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse { + request_id, + reply_to, + reply: v3::registration::RegistredData { + wg_port, + pub_key, + private_ip: ipv4.into() + } + }) + ); + } + + #[test] + fn upgrade_remaining_bandwidth_resp() { + let available_bandwidth = 42; + let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData { + available_bandwidth, + }); + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth( + remaining_bandwidth_data, + reply_to, + request_id, + ); + let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap(); + + assert_eq!( + upgraded_msg.protocol, + Protocol { + version: 4, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + upgraded_msg.data, + v4::response::AuthenticatorResponseData::RemainingBandwidth( + v4::response::RemainingBandwidthResponse { + request_id, + reply_to, + reply: Some(v4::registration::RemainingBandwidthData { + available_bandwidth, + }) + } + ) + ); + } + + #[test] + fn downgrade_remaining_bandwidth_resp() { + let available_bandwidth = 42; + let remaining_bandwidth_data = Some(v4::registration::RemainingBandwidthData { + available_bandwidth, + }); + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v4::response::AuthenticatorResponse::new_remaining_bandwidth( + remaining_bandwidth_data, + reply_to, + request_id, + ); + let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap(); + + assert_eq!( + downgraded_msg.protocol, + Protocol { + version: 3, + service_provider_type: ServiceProviderType::Authenticator + } + ); + assert_eq!( + downgraded_msg.data, + v3::response::AuthenticatorResponseData::RemainingBandwidth( + v3::response::RemainingBandwidthResponse { + request_id, + reply_to, + reply: Some(v3::registration::RemainingBandwidthData { + available_bandwidth, + }) + } + ) + ); + } + + #[test] + fn downgrade_topup_resp() { + let available_bandwidth = 42; + let remaining_bandwidth_data = v4::registration::RemainingBandwidthData { + available_bandwidth, + }; + let request_id = 123; + let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap(); + + let msg = v4::response::AuthenticatorResponse::new_topup_bandwidth( + remaining_bandwidth_data, + reply_to, + request_id, + ); + assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err()); + } +} diff --git a/common/authenticator-requests/src/v4/registration.rs b/common/authenticator-requests/src/v4/registration.rs index a4a49c66a5..d6e2ee9682 100644 --- a/common/authenticator-requests/src/v4/registration.rs +++ b/common/authenticator-requests/src/v4/registration.rs @@ -28,7 +28,7 @@ pub type HmacSha256 = Hmac; pub type Nonce = u64; pub type Taken = Option; -pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB +pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct IpPair { @@ -81,7 +81,7 @@ impl From for IpPair { } } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct InitMessage { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, @@ -93,7 +93,7 @@ impl InitMessage { } } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct FinalMessage { /// Gateway client data pub gateway_client: GatewayClient, @@ -102,28 +102,28 @@ pub struct FinalMessage { pub credential: Option, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RegistrationData { pub nonce: u64, pub gateway_data: GatewayClient, pub wg_port: u16, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RegistredData { pub pub_key: PeerPublicKey, pub private_ips: IpPair, pub wg_port: u16, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct RemainingBandwidthData { pub available_bandwidth: i64, } /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Gateway/Nym node can then verify pub_key payload using the same process -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct GatewayClient { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, @@ -199,7 +199,7 @@ impl GatewayClient { // TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO2: rely on our internal crypto/hmac -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ClientMac(Vec); impl fmt::Display for ClientMac { diff --git a/common/authenticator-requests/src/v4/request.rs b/common/authenticator-requests/src/v4/request.rs index aa4862b057..25cf0e0f1f 100644 --- a/common/authenticator-requests/src/v4/request.rs +++ b/common/authenticator-requests/src/v4/request.rs @@ -20,7 +20,7 @@ fn generate_random() -> u64 { rng.next_u64() } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct AuthenticatorRequest { pub protocol: Protocol, pub data: AuthenticatorRequestData, @@ -106,7 +106,7 @@ impl AuthenticatorRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum AuthenticatorRequestData { Initial(InitMessage), Final(Box), diff --git a/common/authenticator-requests/src/v4/response.rs b/common/authenticator-requests/src/v4/response.rs index 370fc64671..9743e8db43 100644 --- a/common/authenticator-requests/src/v4/response.rs +++ b/common/authenticator-requests/src/v4/response.rs @@ -10,7 +10,7 @@ use crate::make_bincode_serializer; use super::VERSION; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct AuthenticatorResponse { pub protocol: Protocol, pub data: AuthenticatorResponseData, @@ -120,7 +120,7 @@ impl AuthenticatorResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum AuthenticatorResponseData { PendingRegistration(PendingRegistrationResponse), Registered(RegisteredResponse), @@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData { TopUpBandwidth(TopUpBandwidthResponse), } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct PendingRegistrationResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: RegistrationData, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RegisteredResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: RegistredData, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct RemainingBandwidthResponse { pub request_id: u64, pub reply_to: Recipient, pub reply: Option, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct TopUpBandwidthResponse { pub request_id: u64, pub reply_to: Recipient, diff --git a/common/authenticator-requests/src/v4/topup.rs b/common/authenticator-requests/src/v4/topup.rs index 31a61a0659..1163d07f12 100644 --- a/common/authenticator-requests/src/v4/topup.rs +++ b/common/authenticator-requests/src/v4/topup.rs @@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData; use nym_wireguard_types::PeerPublicKey; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct TopUpMessage { /// Base64 encoded x25519 public key pub pub_key: PeerPublicKey, diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index 754eb61440..b5191eef80 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -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" } diff --git a/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs b/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs index 4b32c60936..d2d1943101 100644 --- a/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs +++ b/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs @@ -8,7 +8,10 @@ use crate::{ }, }; use log::{debug, error}; -use sqlx::ConnectOptions; +use sqlx::{ + sqlite::{SqliteAutoVacuum, SqliteSynchronous}, + ConnectOptions, +}; use std::path::Path; #[derive(Debug, Clone)] @@ -30,6 +33,9 @@ impl StorageManager { } let opts = sqlx::sqlite::SqliteConnectOptions::new() + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) + .synchronous(SqliteSynchronous::Normal) + .auto_vacuum(SqliteAutoVacuum::Incremental) .filename(database_path) .create_if_missing(true) .disable_statement_logging(); @@ -110,7 +116,7 @@ impl StorageManager { ) -> Result<(), sqlx::Error> { sqlx::query!( r#" - INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type) + INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type) VALUES (?, ?, ?) "#, registered_gateway.gateway_id_bs58, @@ -224,7 +230,7 @@ impl StorageManager { ) -> Result<(), sqlx::Error> { sqlx::query!( r#" - INSERT INTO custom_gateway_details(gateway_id_bs58, data) + INSERT INTO custom_gateway_details(gateway_id_bs58, data) VALUES (?, ?) "#, custom.gateway_id_bs58, diff --git a/common/client-core/src/cli_helpers/client_add_gateway.rs b/common/client-core/src/cli_helpers/client_add_gateway.rs index 022db8b9c3..56b1c3ad8e 100644 --- a/common/client-core/src/cli_helpers/client_add_gateway.rs +++ b/common/client-core/src/cli_helpers/client_add_gateway.rs @@ -115,8 +115,13 @@ where hardcoded_topology.get_gateways() } else { let mut rng = rand::thread_rng(); - crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent) - .await? + crate::init::helpers::current_gateways( + &mut rng, + &core.client.nym_api_urls, + user_agent, + core.debug.topology.minimum_gateway_performance, + ) + .await? }; // since we're registering with a brand new gateway, diff --git a/common/client-core/src/cli_helpers/client_init.rs b/common/client-core/src/cli_helpers/client_init.rs index 0e3f3bf234..060c1192da 100644 --- a/common/client-core/src/cli_helpers/client_init.rs +++ b/common/client-core/src/cli_helpers/client_init.rs @@ -170,8 +170,13 @@ where hardcoded_topology.get_gateways() } else { let mut rng = rand::thread_rng(); - crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent) - .await? + crate::init::helpers::current_gateways( + &mut rng, + &core.client.nym_api_urls, + user_agent, + core.debug.topology.minimum_gateway_performance, + ) + .await? }; let gateway_setup = GatewaySetup::New { diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 856ddd9497..bb0ffd5d03 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -32,7 +32,7 @@ use crate::init::{ setup_gateway, types::{GatewaySetup, InitialisationResult}, }; -use crate::{config, spawn_future}; +use crate::{config, spawn_future, ForgetMe}; use futures::channel::mpsc; use log::*; use nym_bandwidth_controller::BandwidthController; @@ -188,6 +188,11 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> { user_agent: Option, setup_method: GatewaySetup, + + #[cfg(unix)] + connection_fd_callback: Option>, + + forget_me: ForgetMe, } impl<'a, C, S> BaseClientBuilder<'a, C, S> @@ -210,9 +215,18 @@ where shutdown: None, user_agent: None, setup_method: GatewaySetup::MustLoad { gateway_id: None }, + #[cfg(unix)] + connection_fd_callback: None, + forget_me: Default::default(), } } + #[must_use] + pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self { + self.forget_me = forget_me.clone(); + self + } + #[must_use] pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self { self.setup_method = setup; @@ -261,6 +275,15 @@ where Ok(self) } + #[cfg(unix)] + pub fn with_connection_fd_callback( + mut self, + callback: Arc, + ) -> Self { + self.connection_fd_callback = Some(callback); + self + } + // note: do **NOT** make this method public as its only valid usage is from within `start_base` // because it relies on the crypto keys being already loaded fn mix_address(details: &InitialisationResult) -> Recipient { @@ -352,6 +375,7 @@ where controller.start_with_shutdown(shutdown) } + #[allow(clippy::too_many_arguments)] async fn start_gateway_client( config: &Config, initialisation_result: InitialisationResult, @@ -359,6 +383,7 @@ where details_store: &S::GatewaysDetailsStore, packet_router: PacketRouter, stats_reporter: ClientStatsSender, + #[cfg(unix)] connection_fd_callback: Option>, shutdown: TaskClient, ) -> Result, ClientCoreError> where @@ -401,6 +426,8 @@ where packet_router, bandwidth_controller, stats_reporter, + #[cfg(unix)] + connection_fd_callback, shutdown, ) }; @@ -462,6 +489,7 @@ where details_store: &S::GatewaysDetailsStore, packet_router: PacketRouter, stats_reporter: ClientStatsSender, + #[cfg(unix)] connection_fd_callback: Option>, mut shutdown: TaskClient, ) -> Result, ClientCoreError> where @@ -493,6 +521,8 @@ where details_store, packet_router, stats_reporter, + #[cfg(unix)] + connection_fd_callback, shutdown, ) .await?; @@ -615,9 +645,11 @@ where fn start_mix_traffic_controller( gateway_transceiver: Box, shutdown: TaskClient, + forget_me: ForgetMe, ) -> BatchMixMessageSender { info!("Starting mix traffic controller..."); - let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_transceiver); + let (mix_traffic_controller, mix_tx) = + MixTrafficController::new(gateway_transceiver, forget_me); mix_traffic_controller.start_with_shutdown(shutdown); mix_tx } @@ -772,6 +804,8 @@ where &details_store, gateway_packet_router, stats_reporter.clone(), + #[cfg(unix)] + self.connection_fd_callback, shutdown.fork("gateway_transceiver"), ) .await?; @@ -797,9 +831,11 @@ where // that are to be sent to the mixnet. They are used by cover traffic stream and real // traffic stream. // The MixTrafficController then sends the actual traffic + let message_sender = Self::start_mix_traffic_controller( gateway_transceiver, shutdown.fork("mix_traffic_controller"), + self.forget_me, ); // Channels that the websocket listener can use to signal downstream to the real traffic diff --git a/common/client-core/src/client/mix_traffic/mod.rs b/common/client-core/src/client/mix_traffic/mod.rs index 91c652efba..fa73cc82a8 100644 --- a/common/client-core/src/client/mix_traffic/mod.rs +++ b/common/client-core/src/client/mix_traffic/mod.rs @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::client::mix_traffic::transceiver::GatewayTransceiver; -use crate::spawn_future; +use crate::{spawn_future, ForgetMe}; use log::*; +use nym_gateway_requests::ClientRequest; use nym_sphinx::forwarding::packet::MixPacket; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender>; @@ -26,10 +27,14 @@ pub struct MixTrafficController { // TODO: this is temporary work-around. // in long run `gateway_client` will be moved away from `MixTrafficController` anyway. consecutive_gateway_failure_count: usize, + forget_me: ForgetMe, } impl MixTrafficController { - pub fn new(gateway_transceiver: T) -> (MixTrafficController, BatchMixMessageSender) + pub fn new( + gateway_transceiver: T, + forget_me: ForgetMe, + ) -> (MixTrafficController, BatchMixMessageSender) where T: GatewayTransceiver + Send + 'static, { @@ -40,6 +45,7 @@ impl MixTrafficController { gateway_transceiver: Box::new(gateway_transceiver), mix_rx: message_receiver, consecutive_gateway_failure_count: 0, + forget_me, }, message_sender, ) @@ -47,6 +53,7 @@ impl MixTrafficController { pub fn new_dynamic( gateway_transceiver: Box, + forget_me: ForgetMe, ) -> (MixTrafficController, BatchMixMessageSender) { let (message_sender, message_receiver) = tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE); @@ -55,6 +62,7 @@ impl MixTrafficController { gateway_transceiver, mix_rx: message_receiver, consecutive_gateway_failure_count: 0, + forget_me, }, message_sender, ) @@ -111,7 +119,27 @@ impl MixTrafficController { } } shutdown.recv_timeout().await; + + if self.forget_me.any() { + log::info!("Sending forget me request to the gateway"); + match self + .gateway_transceiver + .send_client_request(ClientRequest::ForgetMe { + client: self.forget_me.client(), + stats: self.forget_me.stats(), + }) + .await + { + Ok(_) => { + log::info!("Successfully sent forget me request to the gateway"); + } + Err(err) => { + log::error!("Failed to send forget me request to the gateway: {err}"); + } + } + } + log::debug!("MixTrafficController: Exiting"); - }) + }); } } diff --git a/common/client-core/src/client/mix_traffic/transceiver.rs b/common/client-core/src/client/mix_traffic/transceiver.rs index 0862911d93..77c226d245 100644 --- a/common/client-core/src/client/mix_traffic/transceiver.rs +++ b/common/client-core/src/client/mix_traffic/transceiver.rs @@ -5,8 +5,10 @@ use async_trait::async_trait; use log::{debug, error}; use nym_credential_storage::storage::Storage as CredentialStorage; use nym_crypto::asymmetric::identity; +use nym_gateway_client::error::GatewayClientError; use nym_gateway_client::GatewayClient; pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter}; +use nym_gateway_requests::ClientRequest; use nym_sphinx::forwarding::packet::MixPacket; use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use std::fmt::Debug; @@ -14,7 +16,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)] @@ -26,9 +28,14 @@ fn erase_err(err: E) -> ErasedGate } /// This combines combines the functionalities of being able to send and receive mix packets. +#[async_trait] pub trait GatewayTransceiver: GatewaySender + GatewayReceiver { fn gateway_identity(&self) -> identity::PublicKey; fn ws_fd(&self) -> Option; + async fn send_client_request( + &mut self, + message: ClientRequest, + ) -> Result<(), GatewayClientError>; } /// This trait defines the functionality of sending `MixPacket` into the mixnet, @@ -65,6 +72,7 @@ pub trait GatewayReceiver { } // to allow for dynamic dispatch +#[async_trait] impl GatewayTransceiver for Box { #[inline] fn gateway_identity(&self) -> identity::PublicKey { @@ -73,6 +81,13 @@ impl GatewayTransceiver for Box { fn ws_fd(&self) -> Option { (**self).ws_fd() } + + async fn send_client_request( + &mut self, + message: ClientRequest, + ) -> Result<(), GatewayClientError> { + (**self).send_client_request(message).await + } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -91,7 +106,6 @@ impl GatewaySender for Box { (**self).batch_send_mix_packets(packets).await } } - impl GatewayReceiver for Box { #[inline] fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> { @@ -111,6 +125,7 @@ impl RemoteGateway { } } +#[async_trait] impl GatewayTransceiver for RemoteGateway where C: DkgQueryClient + Send + Sync, @@ -123,6 +138,20 @@ where fn ws_fd(&self) -> Option { self.gateway_client.ws_fd() } + + async fn send_client_request( + &mut self, + message: ClientRequest, + ) -> Result<(), GatewayClientError> { + if let Some(shared_key) = self.gateway_client.shared_key() { + self.gateway_client + .send_websocket_message(message.encrypt(&*shared_key)?) + .await?; + Ok(()) + } else { + Err(GatewayClientError::ConnectionInInvalidState) + } + } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -170,7 +199,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, + packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender, // 'receiver' part packet_router_tx: Option>, @@ -180,7 +209,7 @@ pub struct LocalGateway { impl LocalGateway { pub fn new( local_identity: identity::PublicKey, - packet_forwarder: mpsc::UnboundedSender, + packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender, packet_router_tx: oneshot::Sender, ) -> Self { LocalGateway { @@ -195,6 +224,7 @@ impl LocalGateway { mod nonwasm_sealed { use super::*; + #[async_trait] impl GatewayTransceiver for LocalGateway { fn gateway_identity(&self) -> identity::PublicKey { self.local_identity @@ -202,14 +232,20 @@ mod nonwasm_sealed { fn ws_fd(&self) -> Option { None } + + async fn send_client_request( + &mut self, + _message: ClientRequest, + ) -> Result<(), GatewayClientError> { + Ok(()) + } } #[async_trait] 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) } } @@ -270,6 +306,7 @@ impl GatewaySender for MockGateway { } } +#[async_trait] impl GatewayTransceiver for MockGateway { fn gateway_identity(&self) -> identity::PublicKey { self.dummy_identity @@ -277,4 +314,11 @@ impl GatewayTransceiver for MockGateway { fn ws_fd(&self) -> Option { None } + + async fn send_client_request( + &mut self, + _message: ClientRequest, + ) -> Result<(), GatewayClientError> { + Ok(()) + } } diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 60c692df9f..16aa042045 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -7,7 +7,7 @@ use futures::{SinkExt, StreamExt}; use log::{debug, info, trace, warn}; use nym_crypto::asymmetric::identity; use nym_gateway_client::GatewayClient; -use nym_topology::{gateway, mix}; +use nym_topology::gateway; use nym_validator_client::client::IdentityKeyRef; use nym_validator_client::UserAgent; use rand::{seq::SliceRandom, Rng}; @@ -82,6 +82,7 @@ pub async fn current_gateways( rng: &mut R, nym_apis: &[Url], user_agent: Option, + minimum_performance: u8, ) -> Result, ClientCoreError> { let nym_api = nym_apis .choose(rng) @@ -95,41 +96,26 @@ pub async fn current_gateways( log::debug!("Fetching list of gateways from: {nym_api}"); let gateways = client.get_all_basic_entry_assigned_nodes().await?; - log::debug!("Found {} gateways", gateways.len()); + info!("nym api reports {} gateways", gateways.len()); + log::trace!("Gateways: {:#?}", gateways); let valid_gateways = gateways .iter() + .filter(|g| g.performance.round_to_integer() >= minimum_performance) .filter_map(|gateway| gateway.try_into().ok()) .collect::>(); log::debug!("After checking validity: {}", valid_gateways.len()); log::trace!("Valid gateways: {:#?}", valid_gateways); - log::info!("nym-api reports {} valid gateways", valid_gateways.len()); + log::info!( + "and {} after validity and performance filtering", + valid_gateways.len() + ); Ok(valid_gateways) } -pub async fn current_mixnodes( - rng: &mut R, - nym_apis: &[Url], -) -> Result, ClientCoreError> { - let nym_api = nym_apis - .choose(rng) - .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; - let client = nym_validator_client::client::NymApiClient::new(nym_api.clone()); - - log::trace!("Fetching list of mixnodes from: {nym_api}"); - - let mixnodes = client.get_all_basic_active_mixing_assigned_nodes().await?; - let valid_mixnodes = mixnodes - .iter() - .filter_map(|mixnode| mixnode.try_into().ok()) - .collect::>(); - - Ok(valid_mixnodes) -} - #[cfg(not(target_arch = "wasm32"))] async fn connect(endpoint: &str) -> Result { match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await { @@ -204,7 +190,7 @@ where Ok(GatewayWithLatency::new(gateway, avg)) } -pub async fn choose_gateway_by_latency<'a, R: Rng, G: ConnectableGateway + Clone>( +pub async fn choose_gateway_by_latency( rng: &mut R, gateways: &[G], must_use_tls: bool, diff --git a/common/client-core/src/lib.rs b/common/client-core/src/lib.rs index ffa1402859..12ea3f7d5c 100644 --- a/common/client-core/src/lib.rs +++ b/common/client-core/src/lib.rs @@ -34,3 +34,48 @@ where { tokio::spawn(future); } + +#[derive(Clone, Default, Debug)] +pub struct ForgetMe { + client: bool, + stats: bool, +} + +impl ForgetMe { + pub fn new_all() -> Self { + Self { + client: true, + stats: true, + } + } + + pub fn new_client() -> Self { + Self { + client: true, + stats: false, + } + } + + pub fn new_stats() -> Self { + Self { + client: false, + stats: true, + } + } + + pub fn new(client: bool, stats: bool) -> Self { + Self { client, stats } + } + + pub fn any(&self) -> bool { + self.client || self.stats + } + + pub fn client(&self) -> bool { + self.client + } + + pub fn stats(&self) -> bool { + self.stats + } +} diff --git a/common/client-core/surb-storage/src/backend/fs_backend/manager.rs b/common/client-core/surb-storage/src/backend/fs_backend/manager.rs index b6adadd3d5..02316ddb7f 100644 --- a/common/client-core/surb-storage/src/backend/fs_backend/manager.rs +++ b/common/client-core/surb-storage/src/backend/fs_backend/manager.rs @@ -9,7 +9,10 @@ use crate::backend::fs_backend::{ }, }; use log::{error, info}; -use sqlx::ConnectOptions; +use sqlx::{ + sqlite::{SqliteAutoVacuum, SqliteSynchronous}, + ConnectOptions, +}; use std::path::Path; #[derive(Debug, Clone)] @@ -31,6 +34,9 @@ impl StorageManager { } let opts = sqlx::sqlite::SqliteConnectOptions::new() + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) + .synchronous(SqliteSynchronous::Normal) + .auto_vacuum(SqliteAutoVacuum::Incremental) .filename(database_path) .create_if_missing(fresh) .disable_statement_logging(); diff --git a/common/client-libs/gateway-client/src/client/mod.rs b/common/client-libs/gateway-client/src/client/mod.rs index 6cb7b83f02..26986e3df8 100644 --- a/common/client-libs/gateway-client/src/client/mod.rs +++ b/common/client-libs/gateway-client/src/client/mod.rs @@ -101,6 +101,10 @@ pub struct GatewayClient { // currently unused (but populated) negotiated_protocol: Option, + // Callback on the fd as soon as the connection has been established + #[cfg(unix)] + connection_fd_callback: Option>, + /// Listen to shutdown messages and send notifications back to the task manager task_client: TaskClient, } @@ -116,6 +120,7 @@ impl GatewayClient { packet_router: PacketRouter, bandwidth_controller: Option>, stats_reporter: ClientStatsSender, + #[cfg(unix)] connection_fd_callback: Option>, task_client: TaskClient, ) -> Self { GatewayClient { @@ -131,6 +136,8 @@ impl GatewayClient { bandwidth_controller, stats_reporter, negotiated_protocol: None, + #[cfg(unix)] + connection_fd_callback, task_client, } } @@ -205,6 +212,12 @@ impl GatewayClient { }; self.connection = SocketState::Available(Box::new(ws_stream)); + + #[cfg(unix)] + if let (Some(callback), Some(fd)) = (self.connection_fd_callback.as_ref(), self.ws_fd()) { + callback.as_ref()(fd); + } + Ok(()) } @@ -311,7 +324,7 @@ impl GatewayClient { // If we want to send a message (with response), we need to have a full control over the socket, // as we need to be able to write the request and read the subsequent response - async fn send_websocket_message( + pub async fn send_websocket_message( &mut self, msg: impl Into, ) -> Result { @@ -1034,6 +1047,8 @@ impl GatewayClient { bandwidth_controller: None, stats_reporter: ClientStatsSender::new(None), negotiated_protocol: None, + #[cfg(unix)] + connection_fd_callback: None, task_client, } } @@ -1064,6 +1079,8 @@ impl GatewayClient { bandwidth_controller, stats_reporter, negotiated_protocol: self.negotiated_protocol, + #[cfg(unix)] + connection_fd_callback: self.connection_fd_callback, task_client, } } diff --git a/common/client-libs/mixnet-client/Cargo.toml b/common/client-libs/mixnet-client/Cargo.toml index 68e048d1f0..b240aab513 100644 --- a/common/client-libs/mixnet-client/Cargo.toml +++ b/common/client-libs/mixnet-client/Cargo.toml @@ -8,11 +8,17 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +dashmap = { workspace = true } 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", "sync"] } +tokio-util = { workspace = true, features = ["codec"], optional = true } +tokio-stream = { workspace = 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"] \ No newline at end of file diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index 4da308144f..94e32f8404 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -1,30 +1,33 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use futures::channel::mpsc; +use dashmap::DashMap; use futures::StreamExt; -use log::*; use nym_sphinx::addressing::nodes::NymNodeRoutingAddress; use nym_sphinx::framing::codec::NymCodec; use nym_sphinx::framing::packet::FramedNymPacket; use nym_sphinx::params::PacketType; use nym_sphinx::NymPacket; -use std::collections::HashMap; use std::io; use std::net::SocketAddr; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::ops::Deref; +use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; use tokio::net::TcpStream; +use tokio::sync::mpsc; +use tokio::sync::mpsc::error::TrySendError; use tokio::time::sleep; +use tokio_stream::wrappers::ReceiverStream; 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 +36,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, } } } @@ -57,11 +58,37 @@ pub trait SendWithoutResponse { } pub struct Client { - conn_new: HashMap, + active_connections: ActiveConnections, + connections_count: Arc, config: Config, } -struct ConnectionSender { +#[derive(Default, Clone)] +pub struct ActiveConnections { + inner: Arc>, +} + +impl ActiveConnections { + pub fn pending_packets(&self) -> usize { + self.inner + .iter() + .map(|sender| { + let max_capacity = sender.channel.max_capacity(); + let capacity = sender.channel.capacity(); + max_capacity - capacity + }) + .sum() + } +} + +impl Deref for ActiveConnections { + type Target = DashMap; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +pub struct ConnectionSender { channel: mpsc::Sender, current_reconnection_attempt: Arc, } @@ -75,46 +102,53 @@ impl ConnectionSender { } } -impl Client { - pub fn new(config: Config) -> Client { - Client { - conn_new: HashMap::new(), - config, +struct ManagedConnection { + address: SocketAddr, + message_receiver: ReceiverStream, + connection_timeout: Duration, + current_reconnection: Arc, +} + +impl ManagedConnection { + fn new( + address: SocketAddr, + message_receiver: mpsc::Receiver, + connection_timeout: Duration, + current_reconnection: Arc, + ) -> Self { + ManagedConnection { + address, + message_receiver: ReceiverStream::new(message_receiver), + connection_timeout, + current_reconnection, } } - async fn manage_connection( - address: SocketAddr, - receiver: mpsc::Receiver, - connection_timeout: Duration, - current_reconnection: &AtomicU32, - ) { + async fn run(self) { + let address = self.address; let connection_fut = TcpStream::connect(address); - let conn = match tokio::time::timeout(connection_timeout, connection_fut).await { + let conn = match tokio::time::timeout(self.connection_timeout, connection_fut).await { Ok(stream_res) => match stream_res { Ok(stream) => { - debug!("Managed to establish connection to {}", address); + debug!("Managed to establish connection to {}", self.address); // if we managed to connect, reset the reconnection count (whatever it might have been) - current_reconnection.store(0, Ordering::Release); + self.current_reconnection.store(0, Ordering::Release); Framed::new(stream, NymCodec) } Err(err) => { - debug!( - "failed to establish connection to {} (err: {})", - address, err - ); + debug!("failed to establish connection to {address} (err: {err})",); return; } }, Err(_) => { debug!( - "failed to connect to {} within {:?}", - address, connection_timeout + "failed to connect to {address} within {:?}", + self.connection_timeout ); // we failed to connect - increase reconnection attempt - current_reconnection.fetch_add(1, Ordering::SeqCst); + self.current_reconnection.fetch_add(1, Ordering::SeqCst); return; } }; @@ -122,15 +156,28 @@ impl Client { // Take whatever the receiver channel produces and put it on the connection. // We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care // about neither receiver nor the connection, it doesn't matter which one gets consumed - if let Err(err) = receiver.map(Ok).forward(conn).await { - warn!("Failed to forward packets to {} - {err}", address); + if let Err(err) = self.message_receiver.map(Ok).forward(conn).await { + warn!("Failed to forward packets to {address}: {err}"); } debug!( - "connection manager to {} is finished. Either the connection failed or mixnet client got dropped", - address + "connection manager to {address} is finished. Either the connection failed or mixnet client got dropped", ); } +} + +impl Client { + pub fn new(config: Config, connections_count: Arc) -> Client { + Client { + active_connections: Default::default(), + connections_count, + config, + } + } + + pub fn active_connections(&self) -> ActiveConnections { + self.active_connections.clone() + } /// If we're trying to reconnect, determine how long we should wait. fn determine_backoff(&self, current_attempt: u32) -> Option { @@ -150,7 +197,7 @@ impl Client { } fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) { - let (mut sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size); + let (sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size); // this CAN'T fail because we just created the channel which has a non-zero capacity if self.config.maximum_connection_buffer_size > 0 { @@ -158,15 +205,16 @@ impl Client { } // if we already tried to connect to `address` before, grab the current attempt count - let current_reconnection_attempt = if let Some(existing) = self.conn_new.get_mut(&address) { - existing.channel = sender; - Arc::clone(&existing.current_reconnection_attempt) - } else { - let new_entry = ConnectionSender::new(sender); - let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt); - self.conn_new.insert(address, new_entry); - current_attempt - }; + let current_reconnection_attempt = + if let Some(mut existing) = self.active_connections.get_mut(&address) { + existing.channel = sender; + Arc::clone(&existing.current_reconnection_attempt) + } else { + let new_entry = ConnectionSender::new(sender); + let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt); + self.active_connections.insert(address, new_entry); + current_attempt + }; // load the actual value. let reconnection_attempt = current_reconnection_attempt.load(Ordering::Acquire); @@ -175,6 +223,7 @@ impl Client { // copy the value before moving into another task let initial_connection_timeout = self.config.initial_connection_timeout; + let connections_count = self.connections_count.clone(); tokio::spawn(async move { // before executing the manager, wait for what was specified, if anything if let Some(backoff) = backoff { @@ -182,13 +231,16 @@ impl Client { sleep(backoff).await; } - Self::manage_connection( + connections_count.fetch_add(1, Ordering::SeqCst); + ManagedConnection::new( address.into(), receiver, initial_connection_timeout, - ¤t_reconnection_attempt, + current_reconnection_attempt, ) - .await + .run() + .await; + connections_count.fetch_sub(1, Ordering::SeqCst); }); } } @@ -200,53 +252,50 @@ 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) { - if err.is_full() { - debug!("Connection to {} seems to not be able to handle all the traffic - dropping the current packet", address); - // it's not a 'big' error, but we did not manage to send the packet - // if the queue is full, we can't really do anything but to drop the packet - Err(io::Error::new( - io::ErrorKind::WouldBlock, - "connection queue is full", - )) - } else if err.is_disconnected() { - debug!( - "Connection to {} seems to be dead. attempting to re-establish it...", - address - ); - // it's not a 'big' error, but we did not manage to send the packet, but queue - // it up to send it as soon as the connection is re-established - self.make_connection(address, err.into_inner()); - Err(io::Error::new( - io::ErrorKind::ConnectionAborted, - "reconnection attempt is in progress", - )) - } else { - // this can't really happen, but let's safe-guard against it in case something changes in futures library - Err(io::Error::new( - io::ErrorKind::Other, - "unknown connection buffer error", - )) - } - } else { - Ok(()) - } - } else { + let Some(sender) = self.active_connections.get_mut(&address) else { // there was never a connection to begin with debug!("establishing initial connection to {}", address); // it's not a 'big' error, but we did not manage to send the packet, but queue the packet // for sending for as soon as the connection is created self.make_connection(address, framed_packet); - Err(io::Error::new( + return Err(io::Error::new( io::ErrorKind::NotConnected, "connection is in progress", - )) - } + )); + }; + + let sending_res = sender.channel.try_send(framed_packet); + drop(sender); + + sending_res.map_err(|err| { + match err { + TrySendError::Full(_) => { + debug!("Connection to {address} seems to not be able to handle all the traffic - dropping the current packet"); + // it's not a 'big' error, but we did not manage to send the packet + // if the queue is full, we can't really do anything but to drop the packet + io::Error::new( + io::ErrorKind::WouldBlock, + "connection queue is full", + ) + } + TrySendError::Closed(dropped) => { + debug!( + "Connection to {address} seems to be dead. attempting to re-establish it...", + ); + + // it's not a 'big' error, but we did not manage to send the packet, but queue + // it up to send it as soon as the connection is re-established + self.make_connection(address, dropped); + io::Error::new( + io::ErrorKind::ConnectionAborted, + "reconnection attempt is in progress", + ) + } + } + } ) } } @@ -255,13 +304,15 @@ mod tests { use super::*; fn dummy_client() -> Client { - Client::new(Config { - initial_reconnection_backoff: Duration::from_millis(10_000), - 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, - }) + Client::new( + Config { + initial_reconnection_backoff: Duration::from_millis(10_000), + maximum_reconnection_backoff: Duration::from_millis(300_000), + initial_connection_timeout: Duration::from_millis(1_500), + maximum_connection_buffer_size: 128, + }, + Default::default(), + ) } #[test] diff --git a/common/client-libs/mixnet-client/src/forwarder.rs b/common/client-libs/mixnet-client/src/forwarder.rs index 630cc95663..d7815be92f 100644 --- a/common/client-libs/mixnet-client/src/forwarder.rs +++ b/common/client-libs/mixnet-client/src/forwarder.rs @@ -1,77 +1,72 @@ // Copyright 2021 - Nym Technologies SA // 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; -type MixForwardingReceiver = mpsc::UnboundedReceiver; - -/// 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); - let (packet_sender, packet_receiver) = mpsc::unbounded(); +impl From> for MixForwardingSender { + fn from(tx: mpsc::UnboundedSender) -> 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) -> 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; - 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, +} + +impl From for PacketToForward { + fn from(packet: MixPacket) -> Self { + PacketToForward::new_no_delay(packet) + } +} + +impl From<(MixPacket, Option)> for PacketToForward { + fn from((packet, delay_until): (MixPacket, Option)) -> 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) -> Self { + PacketToForward { + packet, + forward_delay_target, } } + + pub fn new_no_delay(packet: MixPacket) -> Self { + Self::new(packet, None) + } } diff --git a/common/client-libs/mixnet-client/src/lib.rs b/common/client-libs/mixnet-client/src/lib.rs index a63eb5ca03..5f967e0330 100644 --- a/common/client-libs/mixnet-client/src/lib.rs +++ b/common/client-libs/mixnet-client/src/lib.rs @@ -1,7 +1,9 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "client")] pub mod client; pub mod forwarder; +#[cfg(feature = "client")] pub use client::{Client, Config, SendWithoutResponse}; diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index c9bd11775d..d382cb696f 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -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" } diff --git a/common/commands/src/internal/ecash/generate_keypair/mod.rs b/common/commands/src/internal/ecash/generate_keypair/mod.rs new file mode 100644 index 0000000000..7be40d986d --- /dev/null +++ b/common/commands/src/internal/ecash/generate_keypair/mod.rs @@ -0,0 +1,41 @@ +// Copyright 2024 - Nym Technologies SA +// 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, +} + +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(()) +} diff --git a/common/commands/src/internal/ecash/mod.rs b/common/commands/src/internal/ecash/mod.rs new file mode 100644 index 0000000000..7a9ab05d9b --- /dev/null +++ b/common/commands/src/internal/ecash/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2024 - Nym Technologies SA +// 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), +} diff --git a/common/commands/src/internal/ecash/withdrawal_request.rs b/common/commands/src/internal/ecash/withdrawal_request.rs new file mode 100644 index 0000000000..bf56babdd9 --- /dev/null +++ b/common/commands/src/internal/ecash/withdrawal_request.rs @@ -0,0 +1,78 @@ +// Copyright 2024 - Nym Technologies SA +// 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 { + 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, +} + +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(()) +} diff --git a/common/commands/src/internal/mod.rs b/common/commands/src/internal/mod.rs new file mode 100644 index 0000000000..44fa4d2659 --- /dev/null +++ b/common/commands/src/internal/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2024 - Nym Technologies SA +// 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), +} diff --git a/common/commands/src/lib.rs b/common/commands/src/lib.rs index e3670ac572..f48415d7a6 100644 --- a/common/commands/src/lib.rs +++ b/common/commands/src/lib.rs @@ -3,5 +3,6 @@ pub mod context; pub mod ecash; +pub mod internal; pub mod utils; pub mod validator; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/ActiveSetUpdate.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/ActiveSetUpdate.ts new file mode 100644 index 0000000000..20e5640581 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/ActiveSetUpdate.ts @@ -0,0 +1,18 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Specification on how the active set should be updated. + */ +export type ActiveSetUpdate = { +/** + * The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`]) + */ +entry_gateways: number, +/** + * The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`]) + */ +exit_gateways: number, +/** + * The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`]. + */ +mixnodes: number, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts new file mode 100644 index 0000000000..44738d7f32 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GatewayConfigUpdate = { host: string, mix_port: number, clients_port: number, location: string, version: string, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Interval.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Interval.ts new file mode 100644 index 0000000000..5efd2d2dc5 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Interval.ts @@ -0,0 +1,30 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Specification of a rewarding interval. + */ +export type Interval = { +/** + * Monotonously increasing id of this interval. + */ +id: number, +/** + * Number of epochs in this interval. + */ +epochs_in_interval: number, +/** + * The timestamp indicating the start of the current rewarding epoch. + */ +current_epoch_start: string, +/** + * Monotonously increasing id of the current epoch in this interval. + */ +current_epoch_id: number, +/** + * The duration of all epochs in this interval. + */ +epoch_length: { secs: number; nanos: number; }, +/** + * The total amount of elapsed epochs since the first epoch of the first interval. + */ +total_elapsed_epochs: number, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/IntervalRewardParams.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/IntervalRewardParams.ts new file mode 100644 index 0000000000..a582bf138b --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/IntervalRewardParams.ts @@ -0,0 +1,51 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Parameters required by the mix-mining reward distribution that do not change during an interval. + */ +export type IntervalRewardParams = { +/** + * Current value of the rewarding pool. + * It is expected to be constant throughout the interval. + */ +reward_pool: string, +/** + * Current value of the staking supply. + * It is expected to be constant throughout the interval. + */ +staking_supply: string, +/** + * Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set. + * Also known as `beta`. + */ +staking_supply_scale_factor: string, +/** + * Current value of the computed reward budget per epoch, per node. + * It is expected to be constant throughout the interval. + */ +epoch_reward_budget: string, +/** + * Current value of the stake saturation point. + * It is expected to be constant throughout the interval. + */ +stake_saturation_point: string, +/** + * Current value of the sybil resistance percent (`alpha`). + * It is not really expected to be changing very often. + * As a matter of fact, unless there's a very specific reason, it should remain constant. + */ +sybil_resistance: string, +/** + * Current active set work factor. + * It is not really expected to be changing very often. + * As a matter of fact, unless there's a very specific reason, it should remain constant. + */ +active_set_work_factor: string, +/** + * Current maximum interval pool emission. + * Assuming all nodes in the rewarded set are fully saturated and have 100% performance, + * this % of the reward pool would get distributed in rewards to all operators and its delegators. + * It is not really expected to be changing very often. + * As a matter of fact, unless there's a very specific reason, it should remain constant. + */ +interval_pool_emission: string, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts new file mode 100644 index 0000000000..7ed7f7907a --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts @@ -0,0 +1,35 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RewardedSetParams } from "./RewardedSetParams"; + +/** + * Specification on how the rewarding params should be updated. + */ +export type IntervalRewardingParamsUpdate = { +/** + * Defines the new value of the reward pool. + */ +reward_pool: string | null, +/** + * Defines the new value of the staking supply. + */ +staking_supply: string | null, +/** + * Defines the new value of the staking supply scale factor. + */ +staking_supply_scale_factor: string | null, +/** + * Defines the new value of the sybil resistance percent. + */ +sybil_resistance_percent: string | null, +/** + * Defines the new value of the active set work factor. + */ +active_set_work_factor: string | null, +/** + * Defines the new value of the interval pool emission rate. + */ +interval_pool_emission: string | null, +/** + * Defines the parameters of the rewarded set. + */ +rewarded_set_params: RewardedSetParams | null, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/MixNodeConfigUpdate.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/MixNodeConfigUpdate.ts new file mode 100644 index 0000000000..96bd132f52 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/MixNodeConfigUpdate.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MixNodeConfigUpdate = { host: string, mix_port: number, verloc_port: number, http_api_port: number, version: string, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Mixnode.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Mixnode.ts new file mode 100644 index 0000000000..ada169d2b4 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Mixnode.ts @@ -0,0 +1,34 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Information provided by the node operator during bonding that are used to allow other entities to use the services of this node. + */ +export type MixNode = { +/** + * Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com + */ +host: string, +/** + * Port used by this mixnode for listening for mix packets. + */ +mix_port: number, +/** + * Port used by this mixnode for listening for verloc requests. + */ +verloc_port: number, +/** + * Port used by this mixnode for its http(s) API + */ +http_api_port: number, +/** + * Base58-encoded x25519 public key used for sphinx key derivation. + */ +sphinx_key: string, +/** + * Base58-encoded ed25519 EdDSA public key. + */ +identity_key: string, +/** + * The self-reported semver version of this mixnode. + */ +version: string, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NodeConfigUpdate.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NodeConfigUpdate.ts new file mode 100644 index 0000000000..b39d3997e4 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NodeConfigUpdate.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NodeConfigUpdate = { host: string | null, custom_http_port: number | null, restore_default_http_port: boolean, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NodeRewardingParameters.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NodeRewardingParameters.ts new file mode 100644 index 0000000000..8354619ff9 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NodeRewardingParameters.ts @@ -0,0 +1,15 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Parameters used for rewarding particular node. + */ +export type NodeRewardingParameters = { +/** + * Performance of the particular node in the current epoch. + */ +performance: string, +/** + * Amount of work performed by this node in the current epoch + * also known as 'omega' in the paper + */ +work_factor: string, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NymNode.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NymNode.ts new file mode 100644 index 0000000000..a113827957 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/NymNode.ts @@ -0,0 +1,20 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Information provided by the node operator during bonding that are used to allow other entities to use the services of this node. + */ +export type NymNode = { +/** + * Network address of this nym-node, for example 1.1.1.1 or foo.mixnode.com + * that is used to discover other capabilities of this node. + */ +host: string, +/** + * Allow specifying custom port for accessing the http, and thus self-described, api + * of this node for the capabilities discovery. + */ +custom_http_port: number | null, +/** + * Base58-encoded ed25519 EdDSA public key. + */ +identity_key: string, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/PendingMixnodeChanges.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/PendingMixnodeChanges.ts new file mode 100644 index 0000000000..cddbfdba80 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/PendingMixnodeChanges.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PendingMixNodeChanges = { pledge_change: number | null, cost_params_change: number | null, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/PendingNodeChanges.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/PendingNodeChanges.ts new file mode 100644 index 0000000000..b1667a190e --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/PendingNodeChanges.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PendingNodeChanges = { pledge_change: number | null, cost_params_change: number | null, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardEstimate.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardEstimate.ts new file mode 100644 index 0000000000..9eea44b06e --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardEstimate.ts @@ -0,0 +1,20 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RewardEstimate = { +/** + * The amount of **decimal** coins that are going to get distributed to the node, + * i.e. the operator and all its delegators. + */ +total_node_reward: string, +/** + * The share of the reward that is going to get distributed to the node operator. + */ +operator: string, +/** + * The share of the reward that is going to get distributed among the node delegators. + */ +delegates: string, +/** + * The operating cost of this node. Note: it's already included in the operator reward. + */ +operating_cost: string, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardedSetParams.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardedSetParams.ts new file mode 100644 index 0000000000..e1d09ac0e3 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardedSetParams.ts @@ -0,0 +1,19 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RewardedSetParams = { +/** + * The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`]) + */ +entry_gateways: number, +/** + * The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`]) + */ +exit_gateways: number, +/** + * The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`]. + */ +mixnodes: number, +/** + * Number of nodes in the 'standby' set. (i.e. [`Role::Standby`]) + */ +standby: number, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardingParams.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardingParams.ts new file mode 100644 index 0000000000..5c8263bb6d --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/RewardingParams.ts @@ -0,0 +1,12 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { IntervalRewardParams } from "./IntervalRewardParams"; +import type { RewardedSetParams } from "./RewardedSetParams"; + +/** + * Parameters used for reward calculation. + */ +export type RewardingParams = { +/** + * Parameters that should remain unchanged throughout an interval. + */ +interval: IntervalRewardParams, rewarded_set: RewardedSetParams, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Role.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Role.ts new file mode 100644 index 0000000000..3bac0196bd --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/Role.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Role = "EntryGateway" | "Layer1" | "Layer2" | "Layer3" | "ExitGateway" | "Standby"; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/UnbondedMixnode.ts b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/UnbondedMixnode.ts new file mode 100644 index 0000000000..adde1f385a --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/bindings/ts-packages/types/src/types/rust/UnbondedMixnode.ts @@ -0,0 +1,23 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Basic information of a node that used to be part of the mix network but has already unbonded. + */ +export type UnbondedMixnode = { +/** + * Base58-encoded ed25519 EdDSA public key. + */ +identity_key: string, +/** + * Address of the owner of this mixnode. + */ +owner: string, +/** + * Entity who bonded this mixnode on behalf of the owner. + * If exists, it's most likely the address of the vesting contract. + */ +proxy: string | null, +/** + * Block height at which this mixnode has unbonded. + */ +unbonding_height: number, }; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 214e7e95df..1f32e71441 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -863,11 +863,4 @@ pub enum QueryMsg { pub struct MigrateMsg { pub unsafe_skip_state_updates: Option, pub vesting_contract_address: Option, - pub current_nym_node_semver: String, - - #[serde(default)] - pub version_score_weights: OutdatedVersionWeights, - - #[serde(default)] - pub version_score_params: VersionScoreFormulaParams, } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/bindings/ts-packages/types/src/types/rust/Period.ts b/common/cosmwasm-smart-contracts/vesting-contract/bindings/ts-packages/types/src/types/rust/Period.ts new file mode 100644 index 0000000000..8dfc1576da --- /dev/null +++ b/common/cosmwasm-smart-contracts/vesting-contract/bindings/ts-packages/types/src/types/rust/Period.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * The vesting period. + */ +export type Period = "Before" | { "In": number } | "After"; diff --git a/common/credential-storage/src/backends/sqlite.rs b/common/credential-storage/src/backends/sqlite.rs index 9267bbddb3..dec0899064 100644 --- a/common/credential-storage/src/backends/sqlite.rs +++ b/common/credential-storage/src/backends/sqlite.rs @@ -23,6 +23,11 @@ impl SqliteEcashTicketbookManager { SqliteEcashTicketbookManager { connection_pool } } + /// Closes the connection pool. + pub async fn close(&self) { + self.connection_pool.close().await + } + pub(crate) async fn cleanup_expired(&self, deadline: Date) -> Result<(), sqlx::Error> { sqlx::query!( "DELETE FROM ecash_ticketbook WHERE expiration_date <= ?", diff --git a/common/credential-storage/src/ephemeral_storage.rs b/common/credential-storage/src/ephemeral_storage.rs index 91436d4d8c..b6a113f414 100644 --- a/common/credential-storage/src/ephemeral_storage.rs +++ b/common/credential-storage/src/ephemeral_storage.rs @@ -43,6 +43,10 @@ impl Debug for EphemeralStorage { impl Storage for EphemeralStorage { type StorageError = StorageError; + async fn close(&self) { + // nothing to do here + } + async fn cleanup_expired(&self) -> Result<(), Self::StorageError> { self.storage_manager.cleanup_expired().await; Ok(()) diff --git a/common/credential-storage/src/persistent_storage/mod.rs b/common/credential-storage/src/persistent_storage/mod.rs index 80e746b482..32b6f581de 100644 --- a/common/credential-storage/src/persistent_storage/mod.rs +++ b/common/credential-storage/src/persistent_storage/mod.rs @@ -33,7 +33,10 @@ use nym_credentials::{ IssuanceTicketBook, IssuedTicketBook, }; use nym_ecash_time::{ecash_today, Date, EcashTime}; -use sqlx::ConnectOptions; +use sqlx::{ + sqlite::{SqliteAutoVacuum, SqliteSynchronous}, + ConnectOptions, +}; use std::path::Path; use zeroize::Zeroizing; @@ -56,6 +59,9 @@ impl PersistentStorage { ); let opts = sqlx::sqlite::SqliteConnectOptions::new() + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) + .synchronous(SqliteSynchronous::Normal) + .auto_vacuum(SqliteAutoVacuum::Incremental) .filename(database_path) .create_if_missing(true) .disable_statement_logging(); @@ -83,6 +89,10 @@ impl PersistentStorage { impl Storage for PersistentStorage { type StorageError = StorageError; + async fn close(&self) { + self.storage_manager.close().await + } + /// remove all expired ticketbooks and expiration date signatures async fn cleanup_expired(&self) -> Result<(), Self::StorageError> { let ecash_yesterday = ecash_today().date().previous_day().unwrap(); diff --git a/common/credential-storage/src/storage.rs b/common/credential-storage/src/storage.rs index 19ddc44e86..4c0602ea85 100644 --- a/common/credential-storage/src/storage.rs +++ b/common/credential-storage/src/storage.rs @@ -22,6 +22,8 @@ use std::error::Error; pub trait Storage: Send + Sync { type StorageError: Error; + async fn close(&self); + /// remove all expired ticketbooks and expiration date signatures async fn cleanup_expired(&self) -> Result<(), Self::StorageError>; diff --git a/common/credential-verification/src/bandwidth_storage_manager.rs b/common/credential-verification/src/bandwidth_storage_manager.rs index 7c3136e91c..19df1dba6f 100644 --- a/common/credential-verification/src/bandwidth_storage_manager.rs +++ b/common/credential-verification/src/bandwidth_storage_manager.rs @@ -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 { - 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 BandwidthStorageManager { +impl BandwidthStorageManager { pub fn new( - storage: S, + storage: GatewayStorage, client_bandwidth: ClientBandwidth, client_id: i64, bandwidth_cfg: BandwidthFlushingBehaviourConfig, diff --git a/common/credential-verification/src/ecash/credential_sender.rs b/common/credential-verification/src/ecash/credential_sender.rs index dc304cf86f..140e3e4b4d 100644 --- a/common/credential-verification/src/ecash/credential_sender.rs +++ b/common/credential-verification/src/ecash/credential_sender.rs @@ -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 { +pub(crate) struct CredentialHandler { config: CredentialHandlerConfig, multisig_threshold: f32, ticket_receiver: UnboundedReceiver, - shared_state: SharedState, + shared_state: SharedState, pending_tickets: Vec, pending_redemptions: Vec, } -impl CredentialHandler -where - St: Storage + Clone + 'static, -{ +impl CredentialHandler { async fn rebuild_pending_tickets( - shared_state: &SharedState, + shared_state: &SharedState, ) -> Result, 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, + shared_state: &SharedState, ) -> Result, 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, - shared_state: SharedState, + shared_state: SharedState, ) -> Result { let multisig_threshold = shared_state .nyxd_client diff --git a/common/credential-verification/src/ecash/error.rs b/common/credential-verification/src/ecash/error.rs index 9e025316a7..ff85a32c3e 100644 --- a/common/credential-verification/src/ecash/error.rs +++ b/common/credential-verification/src/ecash/error.rs @@ -1,7 +1,7 @@ // Copyright 2024 - Nym Technologies SA // 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}")] diff --git a/common/credential-verification/src/ecash/mod.rs b/common/credential-verification/src/ecash/mod.rs index 7a2a7b133a..71d0d993ff 100644 --- a/common/credential-verification/src/ecash/mod.rs +++ b/common/credential-verification/src/ecash/mod.rs @@ -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 { - shared_state: SharedState, +pub struct EcashManager { + shared_state: SharedState, pk_bytes: [u8; 32], // bytes representation of a pub key representing the verifier pay_infos: Mutex>, cred_sender: UnboundedSender, } -impl EcashManager -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 { 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 } diff --git a/common/credential-verification/src/ecash/state.rs b/common/credential-verification/src/ecash/state.rs index 7f5a718759..4c718c3c30 100644 --- a/common/credential-verification/src/ecash/state.rs +++ b/common/credential-verification/src/ecash/state.rs @@ -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 { +pub(crate) struct SharedState { pub(crate) nyxd_client: Arc>, pub(crate) address: AccountId, pub(crate) epoch_data: Arc>>, - pub(crate) storage: S, + pub(crate) storage: GatewayStorage, } -impl SharedState -where - S: Storage + Clone, -{ +impl SharedState { pub(crate) async fn new( nyxd_client: DirectSigningHttpRpcNyxdClient, - storage: S, + storage: GatewayStorage, ) -> Result { let address = nyxd_client.address(); diff --git a/common/credential-verification/src/error.rs b/common/credential-verification/src/error.rs index 0f6b0e27e5..34dbdb53b6 100644 --- a/common/credential-verification/src/error.rs +++ b/common/credential-verification/src/error.rs @@ -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), diff --git a/common/credential-verification/src/lib.rs b/common/credential-verification/src/lib.rs index 066953fc55..50d6db1e05 100644 --- a/common/credential-verification/src/lib.rs +++ b/common/credential-verification/src/lib.rs @@ -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 { +pub struct CredentialVerifier { credential: CredentialSpendingRequest, - ecash_verifier: Arc>, - bandwidth_storage_manager: BandwidthStorageManager, + ecash_verifier: Arc, + bandwidth_storage_manager: BandwidthStorageManager, } -impl CredentialVerifier { +impl CredentialVerifier { pub fn new( credential: CredentialSpendingRequest, - ecash_verifier: Arc>, - bandwidth_storage_manager: BandwidthStorageManager, + ecash_verifier: Arc, + bandwidth_storage_manager: BandwidthStorageManager, ) -> Self { CredentialVerifier { credential, diff --git a/common/credentials-interface/src/lib.rs b/common/credentials-interface/src/lib.rs index 651da25ed6..cf1b138677 100644 --- a/common/credentials-interface/src/lib.rs +++ b/common/credentials-interface/src/lib.rs @@ -225,8 +225,10 @@ impl From for NymPayInfo { Clone, Debug, PartialEq, + Eq, Serialize, Deserialize, + Hash, strum::Display, strum::EnumString, strum::EnumIter, diff --git a/common/gateway-requests/src/registration/handshake/client.rs b/common/gateway-requests/src/registration/handshake/client.rs index 5bdb239a66..549cddca39 100644 --- a/common/gateway-requests/src/registration/handshake/client.rs +++ b/common/gateway-requests/src/registration/handshake/client.rs @@ -9,7 +9,7 @@ use futures::{Sink, Stream}; use rand::{CryptoRng, RngCore}; use tungstenite::Message as WsMessage; -impl<'a, S, R> State<'a, S, R> { +impl State<'_, S, R> { async fn client_handshake_inner(&mut self) -> Result<(), HandshakeError> where S: Stream + Sink + Unpin, diff --git a/common/gateway-requests/src/registration/handshake/gateway.rs b/common/gateway-requests/src/registration/handshake/gateway.rs index fc439b53c0..5fec717c46 100644 --- a/common/gateway-requests/src/registration/handshake/gateway.rs +++ b/common/gateway-requests/src/registration/handshake/gateway.rs @@ -10,7 +10,7 @@ use crate::registration::handshake::{error::HandshakeError, WsItem}; use futures::{Sink, Stream}; use tungstenite::Message as WsMessage; -impl<'a, S, R> State<'a, S, R> { +impl State<'_, S, R> { async fn gateway_handshake_inner( &mut self, raw_init_message: Vec, diff --git a/common/gateway-requests/src/types/text_request.rs b/common/gateway-requests/src/types/text_request.rs index 8be56bcfb3..72eebc3e17 100644 --- a/common/gateway-requests/src/types/text_request.rs +++ b/common/gateway-requests/src/types/text_request.rs @@ -20,6 +20,10 @@ pub enum ClientRequest { hkdf_salt: Vec, derived_key_digest: Vec, }, + ForgetMe { + client: bool, + stats: bool, + }, } impl ClientRequest { diff --git a/common/gateway-requests/src/types/text_response.rs b/common/gateway-requests/src/types/text_response.rs index b0c8250f1e..5c6ce668b5 100644 --- a/common/gateway-requests/src/types/text_response.rs +++ b/common/gateway-requests/src/types/text_response.rs @@ -11,6 +11,7 @@ use tungstenite::Message; #[non_exhaustive] pub enum SensitiveServerResponse { KeyUpgradeAck {}, + ForgetMeAck {}, } impl SensitiveServerResponse { diff --git a/common/gateway-stats-storage/Cargo.toml b/common/gateway-stats-storage/Cargo.toml index d439b34a16..0a36798e52 100644 --- a/common/gateway-stats-storage/Cargo.toml +++ b/common/gateway-stats-storage/Cargo.toml @@ -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] diff --git a/common/gateway-stats-storage/src/lib.rs b/common/gateway-stats-storage/src/lib.rs index 453258b606..5bfc658a93 100644 --- a/common/gateway-stats-storage/src/lib.rs +++ b/common/gateway-stats-storage/src/lib.rs @@ -2,10 +2,14 @@ // 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; +use sqlx::{ + sqlite::{SqliteAutoVacuum, SqliteSynchronous}, + ConnectOptions, +}; use std::path::Path; use time::Date; use tracing::{debug, error}; @@ -35,6 +39,9 @@ impl PersistentStatsStorage { // TODO: we can inject here more stuff based on our gateway global config // struct. Maybe different pool size or timeout intervals? let opts = sqlx::sqlite::SqliteConnectOptions::new() + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) + .synchronous(SqliteSynchronous::Normal) + .auto_vacuum(SqliteAutoVacuum::Incremental) .filename(database_path) .create_if_missing(true) .disable_statement_logging(); @@ -70,8 +77,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?) } @@ -115,6 +122,16 @@ impl PersistentStatsStorage { .await?) } + pub async fn delete_unique_user( + &self, + client_address: DestinationAddressBytes, + ) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .delete_unique_user(client_address.as_base58_string()) + .await?) + } + pub async fn insert_active_session( &self, client_address: DestinationAddressBytes, @@ -125,7 +142,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 +154,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?) } diff --git a/common/gateway-stats-storage/src/models.rs b/common/gateway-stats-storage/src/models.rs index 6f53cf429a..5553875fd4 100644 --- a/common/gateway-stats-storage/src/models.rs +++ b/common/gateway-stats-storage/src/models.rs @@ -1,9 +1,11 @@ // Copyright 2024 - Nym Technologies SA // 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 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 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 { - 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 for ActiveSession { fn from(value: StoredActiveSession) -> Self { ActiveSession { diff --git a/common/gateway-stats-storage/src/sessions.rs b/common/gateway-stats-storage/src/sessions.rs index 920ee4d1d5..a919696967 100644 --- a/common/gateway-stats-storage/src/sessions.rs +++ b/common/gateway-stats-storage/src/sessions.rs @@ -71,6 +71,16 @@ impl SessionManager { Ok(()) } + pub(crate) async fn delete_unique_user(&self, client_address_b58: String) -> Result<()> { + sqlx::query!( + "DELETE FROM sessions_unique_users WHERE client_address = ?", + client_address_b58 + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + pub(crate) async fn get_unique_users(&self, date: Date) -> Result> { sqlx::query_scalar!( "SELECT client_address as count FROM sessions_unique_users WHERE day = ?", diff --git a/common/gateway-storage/.sqlx/query-3ea5542b21a41b14276a8fd6b870c61aa0ddd30fee2565803b88c6086bd2a734.json b/common/gateway-storage/.sqlx/query-3ea5542b21a41b14276a8fd6b870c61aa0ddd30fee2565803b88c6086bd2a734.json new file mode 100644 index 0000000000..8c8994d4ca --- /dev/null +++ b/common/gateway-storage/.sqlx/query-3ea5542b21a41b14276a8fd6b870c61aa0ddd30fee2565803b88c6086bd2a734.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM message_store WHERE client_address_bs58 = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "3ea5542b21a41b14276a8fd6b870c61aa0ddd30fee2565803b88c6086bd2a734" +} diff --git a/common/gateway-storage/.sqlx/query-a3cc707995b8215fa77738cd1a55f9e8d251a3e764104d2a54153895dee1a118.json b/common/gateway-storage/.sqlx/query-a3cc707995b8215fa77738cd1a55f9e8d251a3e764104d2a54153895dee1a118.json new file mode 100644 index 0000000000..bc58f0aa1b --- /dev/null +++ b/common/gateway-storage/.sqlx/query-a3cc707995b8215fa77738cd1a55f9e8d251a3e764104d2a54153895dee1a118.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM available_bandwidth WHERE client_id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "a3cc707995b8215fa77738cd1a55f9e8d251a3e764104d2a54153895dee1a118" +} diff --git a/common/gateway-storage/Cargo.toml b/common/gateway-storage/Cargo.toml index d89bac7639..bd4d8b7256 100644 --- a/common/gateway-storage/Cargo.toml +++ b/common/gateway-storage/Cargo.toml @@ -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 } diff --git a/common/gateway-storage/src/bandwidth.rs b/common/gateway-storage/src/bandwidth.rs index 1f1cdce1c8..be64357350 100644 --- a/common/gateway-storage/src/bandwidth.rs +++ b/common/gateway-storage/src/bandwidth.rs @@ -49,6 +49,16 @@ impl BandwidthManager { Ok(()) } + pub(crate) async fn remove_client(&self, client_id: i64) -> Result<(), sqlx::Error> { + sqlx::query!( + "DELETE FROM available_bandwidth WHERE client_id = ?", + client_id + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + /// Set the expiration date of the particular client to the provided date. pub(crate) async fn set_expiration( &self, diff --git a/common/gateway-storage/src/error.rs b/common/gateway-storage/src/error.rs index 408ec245d6..272d86b557 100644 --- a/common/gateway-storage/src/error.rs +++ b/common/gateway-storage/src/error.rs @@ -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), diff --git a/common/gateway-storage/src/inboxes.rs b/common/gateway-storage/src/inboxes.rs index 6c1ac23c47..bdf798aa02 100644 --- a/common/gateway-storage/src/inboxes.rs +++ b/common/gateway-storage/src/inboxes.rs @@ -133,4 +133,17 @@ impl InboxManager { .await?; Ok(()) } + + pub(crate) async fn remove_messages_for_client( + &self, + client_address_bs58: &str, + ) -> Result<(), sqlx::Error> { + sqlx::query!( + "DELETE FROM message_store WHERE client_address_bs58 = ?", + client_address_bs58 + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } } diff --git a/common/gateway-storage/src/lib.rs b/common/gateway-storage/src/lib.rs index 8d5fda0912..d95581f88a 100644 --- a/common/gateway-storage/src/lib.rs +++ b/common/gateway-storage/src/lib.rs @@ -1,10 +1,8 @@ // Copyright 2020 - Nym Technologies SA // 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, @@ -14,7 +12,10 @@ use nym_credentials_interface::ClientTicket; use nym_gateway_requests::shared_key::SharedGatewayKey; use nym_sphinx::DestinationAddressBytes; use shared_keys::SharedKeysManager; -use sqlx::ConnectOptions; +use sqlx::{ + sqlite::{SqliteAutoVacuum, SqliteSynchronous}, + ConnectOptions, +}; use std::path::Path; use tickets::TicketStorageManager; use time::OffsetDateTime; @@ -29,237 +30,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; - - /// 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; - - /// 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, 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, 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, - ) -> 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, - ) -> Result<(Vec, Option), StorageError>; - - /// Removes messages with the specified ids - /// - /// # Arguments - /// - /// * `ids`: ids of the messages to remove - async fn remove_messages(&self, ids: Vec) -> 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, 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; - - 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; - - async fn insert_epoch_signers( - &self, - epoch_id: i64, - signer_ids: Vec, - ) -> Result<(), StorageError>; - - async fn insert_received_ticket( - &self, - client_id: i64, - received_at: OffsetDateTime, - serial_number: Vec, - data: Vec, - ) -> Result; - - // 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; - - 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, StorageError>; - async fn get_all_proposed_tickets_with_sn( - &self, - proposal_id: u32, - ) -> Result, 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, StorageError>; - - async fn get_all_unverified_tickets(&self) -> Result, StorageError>; - async fn get_all_unresolved_proposals(&self) -> Result, StorageError>; - async fn get_votes(&self, ticket_id: i64) -> Result, StorageError>; - - async fn get_signers(&self, epoch_id: i64) -> Result, 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, 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, StorageError>; - - /// Retrieves all wireguard peers. - async fn get_all_wireguard_peers(&self) -> Result, 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 +43,34 @@ pub struct PersistentStorage { wireguard_peer_manager: wireguard_peers::WgPeerManager, } -impl PersistentStorage { +impl GatewayStorage { + #[allow(dead_code)] + pub(crate) fn client_manager(&self) -> &ClientManager { + &self.client_manager + } + + pub(crate) fn shared_key_manager(&self) -> &SharedKeysManager { + &self.shared_key_manager + } + + pub(crate) fn inbox_manager(&self) -> &InboxManager { + &self.inbox_manager + } + + pub(crate) fn bandwidth_manager(&self) -> &BandwidthManager { + &self.bandwidth_manager + } + + #[allow(dead_code)] + pub(crate) fn ticket_manager(&self) -> &TicketStorageManager { + &self.ticket_manager + } + + #[allow(dead_code)] + pub(crate) fn wireguard_peer_manager(&self) -> &wireguard_peers::WgPeerManager { + &self.wireguard_peer_manager + } + /// Initialises `PersistentStorage` using the provided path. /// /// # Arguments @@ -278,7 +80,7 @@ impl PersistentStorage { pub async fn init + Send>( database_path: P, message_retrieval_limit: i64, - ) -> Result { + ) -> Result { debug!( "Attempting to connect to database {:?}", database_path.as_ref().as_os_str() @@ -287,6 +89,9 @@ impl PersistentStorage { // TODO: we can inject here more stuff based on our gateway global config // struct. Maybe different pool size or timeout intervals? let opts = sqlx::sqlite::SqliteConnectOptions::new() + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) + .synchronous(SqliteSynchronous::Normal) + .auto_vacuum(SqliteAutoVacuum::Incremental) .filename(database_path) .create_if_missing(true) .disable_statement_logging(); @@ -307,7 +112,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 +123,37 @@ 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 { + ) -> Result { Ok(self .shared_key_manager .client_id(&client_address.as_base58_string()) .await?) } - async fn insert_shared_keys( + pub async fn handle_forget_me( + &self, + client_address: DestinationAddressBytes, + ) -> Result<(), GatewayStorageError> { + let client_id = self.get_mixnet_client_id(client_address).await?; + self.inbox_manager() + .remove_messages_for_client(&client_address.as_base58_string()) + .await?; + self.bandwidth_manager().remove_client(client_id).await?; + self.shared_key_manager() + .remove_shared_keys(&client_address.as_base58_string()) + .await?; + Ok(()) + } + + pub async fn insert_shared_keys( &self, client_address: DestinationAddressBytes, shared_keys: &SharedGatewayKey, - ) -> Result { + ) -> Result { let client_address_bs58 = client_address.as_base58_string(); let client_id = match self .shared_key_manager @@ -359,10 +178,10 @@ impl Storage for PersistentStorage { Ok(client_id) } - async fn get_shared_keys( + pub async fn get_shared_keys( &self, client_address: DestinationAddressBytes, - ) -> Result, StorageError> { + ) -> Result, GatewayStorageError> { let keys = self .shared_key_manager .get_shared_keys(&client_address.as_base58_string()) @@ -371,37 +190,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, StorageError> { + pub async fn get_client(&self, client_id: i64) -> Result, 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, - ) -> 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, - ) -> Result<(Vec, Option), StorageError> { + ) -> Result<(Vec, Option), GatewayStorageError> { let messages = self .inbox_manager .get_messages(&client_address.as_base58_string(), start_after) @@ -409,87 +228,95 @@ impl Storage for PersistentStorage { Ok(messages) } - async fn remove_messages(&self, ids: Vec) -> Result<(), StorageError> { + pub async fn remove_messages(&self, ids: Vec) -> 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, StorageError> { + ) -> Result, GatewayStorageError> { Ok(self .bandwidth_manager .get_available_bandwidth(client_id) .await?) } - async fn increase_bandwidth(&self, client_id: i64, amount: i64) -> Result { + pub async fn increase_bandwidth( + &self, + client_id: i64, + amount: i64, + ) -> Result { 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 { + pub async fn decrease_bandwidth( + &self, + client_id: i64, + amount: i64, + ) -> Result { 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, - ) -> 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, data: Vec, - ) -> Result { + ) -> Result { // 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 +330,24 @@ impl Storage for PersistentStorage { Ok(ticket_id) } - async fn contains_ticket(&self, serial_number: &[u8]) -> Result { + pub async fn contains_ticket(&self, serial_number: &[u8]) -> Result { 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 +358,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 +372,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, StorageError> { + pub async fn get_all_verified_tickets_with_sn( + &self, + ) -> Result, 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, StorageError> { + ) -> Result, 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 +424,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 +448,13 @@ impl Storage for PersistentStorage { Ok(()) } - async fn latest_proposal(&self) -> Result, StorageError> { + pub async fn latest_proposal(&self) -> Result, GatewayStorageError> { Ok(self.ticket_manager.get_latest_redemption_proposal().await?) } - async fn get_all_unverified_tickets(&self) -> Result, StorageError> { + pub async fn get_all_unverified_tickets( + &self, + ) -> Result, GatewayStorageError> { self.ticket_manager .get_unverified_tickets() .await? @@ -629,29 +463,37 @@ impl Storage for PersistentStorage { .collect() } - async fn get_all_unresolved_proposals(&self) -> Result, StorageError> { + pub async fn get_all_unresolved_proposals(&self) -> Result, GatewayStorageError> { Ok(self .ticket_manager .get_all_unresolved_redemption_proposal_ids() .await?) } - async fn get_votes(&self, ticket_id: i64) -> Result, StorageError> { + pub async fn get_votes(&self, ticket_id: i64) -> Result, GatewayStorageError> { Ok(self .ticket_manager .get_verification_votes(ticket_id) .await?) } - async fn get_signers(&self, epoch_id: i64) -> Result, StorageError> { + pub async fn get_signers(&self, epoch_id: i64) -> Result, 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, StorageError> { + ) -> Result, GatewayStorageError> { let client_id = match self .wireguard_peer_manager .retrieve_peer(&peer.public_key.to_string()) @@ -676,10 +518,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, StorageError> { + ) -> Result, GatewayStorageError> { let peer = self .wireguard_peer_manager .retrieve_peer(peer_public_key) @@ -687,12 +534,21 @@ impl Storage for PersistentStorage { Ok(peer) } - async fn get_all_wireguard_peers(&self) -> Result, StorageError> { + /// Retrieves all wireguard peers. + pub async fn get_all_wireguard_peers(&self) -> Result, 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?; diff --git a/common/gateway-storage/src/models.rs b/common/gateway-storage/src/models.rs index 74a2e5162d..f1bacdcaea 100644 --- a/common/gateway-storage/src/models.rs +++ b/common/gateway-storage/src/models.rs @@ -1,7 +1,7 @@ // Copyright 2021-2024 - Nym Technologies SA // 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 for SharedGatewayKey { - type Error = StorageError; + type Error = GatewayStorageError; fn try_from(value: PersistedSharedKeys) -> Result { 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 for ClientTicket { - type Error = StorageError; + type Error = GatewayStorageError; fn try_from(value: UnverifiedTicketData) -> Result { 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 for WireguardPeer { } impl TryFrom for defguard_wireguard_rs::host::Peer { - type Error = crate::error::StorageError; + type Error = crate::error::GatewayStorageError; fn try_from(value: WireguardPeer) -> Result { Ok(Self { diff --git a/common/http-api-client/src/lib.rs b/common/http-api-client/src/lib.rs index 824f4fa278..c826fe295b 100644 --- a/common/http-api-client/src/lib.rs +++ b/common/http-api-client/src/lib.rs @@ -282,7 +282,7 @@ impl Client { } } - pub async fn create_delete_request( + pub fn create_delete_request( &self, path: PathSegments<'_>, params: Params<'_, K, V>, @@ -324,6 +324,56 @@ impl Client { } } + pub fn create_patch_request( + &self, + path: PathSegments<'_>, + params: Params<'_, K, V>, + json_body: &B, + ) -> RequestBuilder + where + B: Serialize + ?Sized, + K: AsRef, + V: AsRef, + { + let url = sanitize_url(&self.base_url, path, params); + self.reqwest_client.patch(url).json(json_body) + } + + pub async fn send_patch_request( + &self, + path: PathSegments<'_>, + params: Params<'_, K, V>, + json_body: &B, + ) -> Result> + where + B: Serialize + ?Sized, + K: AsRef, + V: AsRef, + E: Display, + { + let url = sanitize_url(&self.base_url, path, params); + + #[cfg(target_arch = "wasm32")] + { + Ok(wasmtimer::tokio::timeout( + self.request_timeout, + self.reqwest_client.patch(url).json(json_body).send(), + ) + .await + .map_err(|_timeout| HttpClientError::RequestTimeout)??) + } + + #[cfg(not(target_arch = "wasm32"))] + { + Ok(self + .reqwest_client + .patch(url) + .json(json_body) + .send() + .await?) + } + } + #[instrument(level = "debug", skip_all)] pub async fn get_json( &self, @@ -372,6 +422,23 @@ impl Client { parse_response(res, false).await } + pub async fn patch_json( + &self, + path: PathSegments<'_>, + params: Params<'_, K, V>, + json_body: &B, + ) -> Result> + where + B: Serialize + ?Sized, + for<'a> T: Deserialize<'a>, + K: AsRef, + V: AsRef, + E: Display + DeserializeOwned, + { + let res = self.send_patch_request(path, params, json_body).await?; + parse_response(res, true).await + } + #[instrument(level = "debug", skip_all)] pub async fn get_json_endpoint(&self, endpoint: S) -> Result> where @@ -466,6 +533,42 @@ impl Client { parse_response(res, false).await } + + pub async fn patch_json_endpoint( + &self, + endpoint: S, + json_body: &B, + ) -> Result> + where + B: Serialize + ?Sized, + for<'a> T: Deserialize<'a>, + E: Display + DeserializeOwned, + S: AsRef, + { + #[cfg(target_arch = "wasm32")] + let res = { + wasmtimer::tokio::timeout( + self.request_timeout, + self.reqwest_client + .patch(self.base_url.join(endpoint.as_ref())?) + .json(json_body) + .send(), + ) + .await + .map_err(|_timeout| HttpClientError::RequestTimeout)?? + }; + + #[cfg(not(target_arch = "wasm32"))] + let res = { + self.reqwest_client + .patch(self.base_url.join(endpoint.as_ref())?) + .json(json_body) + .send() + .await? + }; + + parse_response(res, true).await + } } // define those methods on the trait for nicer extensions (and not having to type the thing twice) diff --git a/common/http-api-client/src/user_agent.rs b/common/http-api-client/src/user_agent.rs index d47cb570c4..eeec96e424 100644 --- a/common/http-api-client/src/user_agent.rs +++ b/common/http-api-client/src/user_agent.rs @@ -1,7 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::fmt; +use std::{fmt, str::FromStr}; use http::HeaderValue; use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned}; @@ -15,6 +15,36 @@ pub struct UserAgent { pub git_commit: String, } +#[derive(Clone, Debug, thiserror::Error)] +#[error("invalid user agent string: {0}")] +pub struct UserAgentError(String); + +impl FromStr for UserAgent { + type Err = UserAgentError; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split('/').collect(); + if parts.len() != 4 { + return Err(UserAgentError(s.to_string())); + } + + Ok(UserAgent { + application: parts[0].to_string(), + version: parts[1].to_string(), + platform: parts[2].to_string(), + git_commit: parts[3].to_string(), + }) + } +} + +impl TryFrom<&str> for UserAgent { + type Error = UserAgentError; + + fn try_from(s: &str) -> Result { + UserAgent::from_str(s) + } +} + impl fmt::Display for UserAgent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let abbreviated_commit = self.git_commit.chars().take(7).collect::(); @@ -55,3 +85,85 @@ impl From for UserAgent { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parsing_valid_user_agent() { + let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg"; + let parsed = UserAgent::from_str(user_agent).unwrap(); + assert_eq!( + parsed, + UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string() + } + ); + } + + #[test] + fn parsing_invalid_user_agent() { + let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu"; + assert!(UserAgent::from_str(user_agent).is_err()); + } + + #[test] + fn converting_user_agent_to_string() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + assert_eq!( + user_agent.to_string(), + "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg" + ); + } + + #[test] + fn converting_user_agent_to_display() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + assert_eq!( + format!("{}", user_agent), + "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg" + ); + } + + #[test] + fn converting_user_agent_to_header_value_fails() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + let header_value: Result = user_agent.clone().try_into(); + assert!(header_value.is_ok()); + } + + #[test] + fn converting_user_agent_to_header_value_has_same_string_representation() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + let header_value: HeaderValue = user_agent.clone().try_into().unwrap(); + assert_eq!(header_value.to_str().unwrap(), user_agent.to_string()); + } +} diff --git a/common/mixnode-common/Cargo.toml b/common/mixnode-common/Cargo.toml index a955aba65d..8fbab39525 100644 --- a/common/mixnode-common/Cargo.toml +++ b/common/mixnode-common/Cargo.toml @@ -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" } + diff --git a/common/mixnode-common/src/lib.rs b/common/mixnode-common/src/lib.rs index 4ab41c6040..ffd6ff8c80 100644 --- a/common/mixnode-common/src/lib.rs +++ b/common/mixnode-common/src/lib.rs @@ -1,4 +1,4 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 + pub mod packet_processor; -pub mod verloc; diff --git a/common/mixnode-common/src/verloc/error.rs b/common/mixnode-common/src/verloc/error.rs deleted file mode 100644 index 4bfe0da48e..0000000000 --- a/common/mixnode-common/src/verloc/error.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// 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 {} diff --git a/common/mixnode-common/src/verloc/measurement.rs b/common/mixnode-common/src/verloc/measurement.rs deleted file mode 100644 index cacf72080a..0000000000 --- a/common/mixnode-common/src/verloc/measurement.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021-2024 - Nym Technologies SA -// 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); - - 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) { - 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()) - } -} diff --git a/common/mixnode-common/src/verloc/mod.rs b/common/mixnode-common/src/verloc/mod.rs deleted file mode 100644 index 1ac4cc8957..0000000000 --- a/common/mixnode-common/src/verloc/mod.rs +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2021-2024 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::verloc::listener::PacketListener; -use crate::verloc::sender::{PacketSender, TestedNode}; -use futures::stream::FuturesUnordered; -use futures::StreamExt; -use log::*; -use nym_crypto::asymmetric::identity; -use nym_network_defaults::mainnet::NYM_API; -use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult}; -use nym_task::TaskClient; -use rand::seq::SliceRandom; -use rand::thread_rng; -use std::net::SocketAddr; -use std::sync::Arc; -use std::time::Duration; -use tokio::task::JoinHandle; -use tokio::time::sleep; -use url::Url; - -use measurement::VerlocStatsUpdateExt; - -// pub use crate::verloc::measurement::{AtomicVerlocResult, Verloc, VerlocResult}; - -pub mod error; -pub(crate) mod listener; -pub(crate) mod measurement; -pub(crate) mod packet; -pub(crate) mod sender; - -// by default all of those are overwritten by config data from mixnodes directly -const DEFAULT_VERLOC_PORT: u16 = 1790; -const DEFAULT_PACKETS_PER_NODE: usize = 100; -const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500); -const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000); -const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50); -const DEFAULT_BATCH_SIZE: usize = 50; -const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12); -const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30); - -#[derive(Clone, Debug)] -pub struct Config { - /// Socket address of this node on which it will be listening for the measurement packets. - listening_address: SocketAddr, - - /// Specifies number of echo packets sent to each node during a measurement run. - packets_per_node: usize, - - /// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test. - packet_timeout: Duration, - - /// Specifies maximum amount of time to wait for the connection to get established. - connection_timeout: Duration, - - /// Specifies delay between subsequent test packets being sent (after receiving a reply). - delay_between_packets: Duration, - - /// Specifies number of nodes being tested at once. - tested_nodes_batch_size: usize, - - /// Specifies delay between subsequent test runs. - testing_interval: Duration, - - /// Specifies delay between attempting to run the measurement again if the previous run failed - /// due to being unable to get the list of nodes. - retry_timeout: Duration, - - /// URLs to the nym apis for obtaining network topology. - nym_api_urls: Vec, -} - -impl Config { - pub fn build() -> ConfigBuilder { - ConfigBuilder::new() - } -} - -#[must_use] -pub struct ConfigBuilder(Config); - -impl ConfigBuilder { - pub fn new() -> ConfigBuilder { - Self::default() - } - - pub fn listening_address(mut self, listening_address: SocketAddr) -> Self { - self.0.listening_address = listening_address; - self - } - - pub fn packets_per_node(mut self, packets_per_node: usize) -> Self { - self.0.packets_per_node = packets_per_node; - self - } - - pub fn packet_timeout(mut self, packet_timeout: Duration) -> Self { - self.0.packet_timeout = packet_timeout; - self - } - - pub fn connection_timeout(mut self, connection_timeout: Duration) -> Self { - self.0.connection_timeout = connection_timeout; - self - } - - pub fn delay_between_packets(mut self, delay_between_packets: Duration) -> Self { - self.0.delay_between_packets = delay_between_packets; - self - } - - pub fn tested_nodes_batch_size(mut self, tested_nodes_batch_size: usize) -> Self { - self.0.tested_nodes_batch_size = tested_nodes_batch_size; - self - } - - pub fn testing_interval(mut self, testing_interval: Duration) -> Self { - self.0.testing_interval = testing_interval; - self - } - - pub fn retry_timeout(mut self, retry_timeout: Duration) -> Self { - self.0.retry_timeout = retry_timeout; - self - } - - pub fn nym_api_urls(mut self, nym_api_urls: Vec) -> Self { - self.0.nym_api_urls = nym_api_urls; - self - } - - pub fn build(self) -> Config { - // panics here are fine as those are only ever constructed at the initial setup - assert!( - !self.0.nym_api_urls.is_empty(), - "at least one validator endpoint must be provided", - ); - self.0 - } -} - -impl Default for ConfigBuilder { - fn default() -> Self { - ConfigBuilder(Config { - listening_address: format!("[::]:{DEFAULT_VERLOC_PORT}").parse().unwrap(), - packets_per_node: DEFAULT_PACKETS_PER_NODE, - packet_timeout: DEFAULT_PACKET_TIMEOUT, - connection_timeout: DEFAULT_CONNECTION_TIMEOUT, - delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS, - tested_nodes_batch_size: DEFAULT_BATCH_SIZE, - testing_interval: DEFAULT_TESTING_INTERVAL, - retry_timeout: DEFAULT_RETRY_TIMEOUT, - nym_api_urls: vec![NYM_API.parse().expect("Invalid default API URL")], - }) - } -} - -pub struct VerlocMeasurer { - config: Config, - packet_sender: Arc, - packet_listener: Arc, - 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, - 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) -> 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::>(); - - // 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::>(); - - // 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, -} diff --git a/common/models/Cargo.toml b/common/models/Cargo.toml deleted file mode 100644 index 3192a83b53..0000000000 --- a/common/models/Cargo.toml +++ /dev/null @@ -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"] } diff --git a/common/models/src/lib.rs b/common/models/src/lib.rs deleted file mode 100644 index 3d85e66947..0000000000 --- a/common/models/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod ns_api; diff --git a/common/network-defaults/src/constants.rs b/common/network-defaults/src/constants.rs index 6723b45e41..e9b70f54e3 100644 --- a/common/network-defaults/src/constants.rs +++ b/common/network-defaults/src/constants.rs @@ -57,3 +57,14 @@ pub mod wireguard { pub const WG_TUN_DEVICE_IP_ADDRESS_V6: Ipv6Addr = Ipv6Addr::new(0xfc01, 0, 0, 0, 0, 0, 0, 0x1); // fc01::1 pub const WG_TUN_DEVICE_NETMASK_V6: u8 = 112; } + +pub mod mixnet_vpn { + use std::net::{Ipv4Addr, Ipv6Addr}; + + // The interface used to route traffic + pub const NYM_TUN_BASE_NAME: &str = "nymtun"; + pub const NYM_TUN_DEVICE_ADDRESS_V4: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 1); + pub const NYM_TUN_DEVICE_NETMASK_V4: Ipv4Addr = Ipv4Addr::new(255, 255, 0, 0); + pub const NYM_TUN_DEVICE_ADDRESS_V6: Ipv6Addr = Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0x1); // fc00::1 + pub const NYM_TUN_DEVICE_NETMASK_V6: &str = "112"; +} diff --git a/common/nonexhaustive-delayqueue/src/lib.rs b/common/nonexhaustive-delayqueue/src/lib.rs index ee8c9f214c..6c4853f675 100644 --- a/common/nonexhaustive-delayqueue/src/lib.rs +++ b/common/nonexhaustive-delayqueue/src/lib.rs @@ -65,6 +65,14 @@ impl NonExhaustiveDelayQueue { pub fn remove(&mut self, key: &QueueKey) -> Expired { self.inner.remove(key) } + + pub fn len(&self) -> usize { + self.inner.len() + } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } } impl Default for NonExhaustiveDelayQueue { diff --git a/common/nym-metrics/Cargo.toml b/common/nym-metrics/Cargo.toml index 4310ae28f6..1ac3bce16a 100644 --- a/common/nym-metrics/Cargo.toml +++ b/common/nym-metrics/Cargo.toml @@ -12,6 +12,6 @@ license.workspace = true [dependencies] prometheus = { workspace = true } -log = { workspace = true } +tracing = { workspace = true } dashmap = { workspace = true } lazy_static = { workspace = true } diff --git a/common/nym-metrics/src/lib.rs b/common/nym-metrics/src/lib.rs index 565d62cd5f..7c6373ccc7 100644 --- a/common/nym-metrics/src/lib.rs +++ b/common/nym-metrics/src/lib.rs @@ -1,14 +1,18 @@ use dashmap::DashMap; -pub use log::error; -use log::{debug, warn}; use std::fmt; -pub use std::time::Instant; +use tracing::{debug, error, warn}; -use prometheus::{core::Collector, Encoder as _, IntCounter, IntGauge, Registry, TextEncoder}; +use prometheus::{ + core::Collector, Encoder as _, Gauge, Histogram, HistogramOpts, IntCounter, IntGauge, Registry, + TextEncoder, +}; + +pub use prometheus::HistogramTimer; +pub use std::time::Instant; #[macro_export] macro_rules! prepend_package_name { - ($name: literal) => { + ($name: tt) => { &format!( "{}_{}", std::module_path!() @@ -23,15 +27,29 @@ macro_rules! prepend_package_name { #[macro_export] macro_rules! inc_by { + ($name:literal, $x:expr, $help: expr) => { + $crate::REGISTRY.maybe_register_and_inc_by( + $crate::prepend_package_name!($name), + $x as i64, + $help, + ); + }; ($name:literal, $x:expr) => { - $crate::REGISTRY.inc_by($crate::prepend_package_name!($name), $x as i64); + $crate::REGISTRY.maybe_register_and_inc_by( + $crate::prepend_package_name!($name), + $x as i64, + None, + ); }; } #[macro_export] macro_rules! inc { + ($name:literal, $help: expr) => { + $crate::REGISTRY.maybe_register_and_inc($crate::prepend_package_name!($name), $help); + }; ($name:literal) => { - $crate::REGISTRY.inc($crate::prepend_package_name!($name)); + $crate::REGISTRY.maybe_register_and_inc($crate::prepend_package_name!($name), None); }; } @@ -42,6 +60,71 @@ macro_rules! metrics { }; } +#[macro_export] +macro_rules! set_metric { + ($name:literal, $x:expr, $help: expr) => { + $crate::REGISTRY.maybe_register_and_set( + $crate::prepend_package_name!($name), + $x as i64, + $help, + ); + }; + ($name:literal, $x:expr) => { + $crate::REGISTRY.maybe_register_and_set( + $crate::prepend_package_name!($name), + $x as i64, + None, + ); + }; +} + +#[macro_export] +macro_rules! set_metric_float { + ($name:literal, $x:expr, $help: expr) => { + $crate::REGISTRY.maybe_register_and_set_float( + $crate::prepend_package_name!($name), + $x as f64, + $help, + ); + }; + ($name:literal, $x:expr) => { + $crate::REGISTRY.maybe_register_and_set_float( + $crate::prepend_package_name!($name), + $x as f64, + None, + ); + }; +} + +#[macro_export] +macro_rules! add_histogram_obs { + ($name:expr, $x:expr, $b:expr, $help:expr) => { + $crate::REGISTRY.maybe_register_and_add_to_histogram( + $crate::prepend_package_name!($name), + $x as f64, + Some($b), + $help, + ); + }; + + ($name:expr, $x:expr, $b:expr) => { + $crate::REGISTRY.maybe_register_and_add_to_histogram( + $crate::prepend_package_name!($name), + $x as f64, + Some($b), + None, + ); + }; + ($name:expr, $x:expr) => { + $crate::REGISTRY.maybe_register_and_add_to_histogram( + $crate::prepend_package_name!($name), + $x as f64, + None, + None, + ); + }; +} + #[macro_export] macro_rules! nanos { ( $name:literal, $x:expr ) => {{ @@ -50,7 +133,7 @@ macro_rules! nanos { let r = $x; let duration = start.elapsed().as_nanos() as i64; let name = $crate::prepend_package_name!($name); - $crate::REGISTRY.inc_by(&format!("{}_nanos", $name), duration); + $crate::REGISTRY.maybe_register_and_inc_by(&format!("{}_nanos", $name), duration, None); r }}; } @@ -59,15 +142,100 @@ lazy_static::lazy_static! { pub static ref REGISTRY: MetricsController = MetricsController::default(); } +pub fn metrics_registry() -> &'static MetricsController { + ®ISTRY +} + #[derive(Default)] pub struct MetricsController { registry: Registry, registry_index: DashMap, } -enum Metric { - C(Box), - G(Box), +pub enum Metric { + IntCounter(Box), + IntGauge(Box), + FloatGauge(Box), + Histogram(Box), +} + +impl Metric { + pub fn new_int_counter(name: &str, help: &str) -> Option { + match IntCounter::new(sanitize_metric_name(name), help) { + Ok(c) => Some(c.into()), + Err(err) => { + error!("Failed to create counter {name:?}: {err}"); + None + } + } + } + + pub fn new_int_gauge(name: &str, help: &str) -> Option { + match IntGauge::new(sanitize_metric_name(name), help) { + Ok(g) => Some(g.into()), + Err(err) => { + error!("Failed to create gauge {name:?}: {err}"); + None + } + } + } + + pub fn new_float_gauge(name: &str, help: &str) -> Option { + match Gauge::new(sanitize_metric_name(name), help) { + Ok(g) => Some(g.into()), + Err(err) => { + error!("Failed to create gauge {name:?}: {err}"); + None + } + } + } + + pub fn new_histogram(name: &str, help: &str, buckets: Option<&[f64]>) -> Option { + let mut opts = HistogramOpts::new(sanitize_metric_name(name), help); + if let Some(buckets) = buckets { + opts = opts.buckets(buckets.to_vec()) + } + match Histogram::with_opts(opts) { + Ok(h) => Some(Metric::Histogram(Box::new(h))), + Err(err) => { + error!("failed to create histogram {name:?}: {err}"); + None + } + } + } + + fn as_collector(&self) -> Box { + match self { + Metric::IntCounter(c) => c.clone(), + Metric::IntGauge(g) => g.clone(), + Metric::FloatGauge(g) => g.clone(), + Metric::Histogram(h) => h.clone(), + } + } +} + +impl From for Metric { + fn from(v: IntCounter) -> Self { + Metric::IntCounter(Box::new(v)) + } +} + +impl From for Metric { + fn from(v: IntGauge) -> Self { + Metric::IntGauge(Box::new(v)) + } +} + +impl From for Metric { + fn from(v: Gauge) -> Self { + Metric::FloatGauge(Box::new(v)) + } +} + +impl From for Metric { + fn from(v: Histogram) -> Self { + Metric::Histogram(Box::new(v)) + } } fn fq_name(c: &dyn Collector) -> String { @@ -81,34 +249,92 @@ impl Metric { #[inline(always)] fn fq_name(&self) -> String { match self { - Metric::C(c) => fq_name(c.as_ref()), - Metric::G(g) => fq_name(g.as_ref()), + Metric::IntCounter(c) => fq_name(c.as_ref()), + Metric::IntGauge(g) => fq_name(g.as_ref()), + Metric::FloatGauge(g) => fq_name(g.as_ref()), + Metric::Histogram(h) => fq_name(h.as_ref()), } } #[inline(always)] fn inc(&self) { match self { - Metric::C(c) => c.inc(), - Metric::G(g) => g.inc(), + Metric::IntCounter(c) => c.inc(), + Metric::IntGauge(g) => g.inc(), + Metric::FloatGauge(g) => g.inc(), + Metric::Histogram(_) => { + warn!("invalid operation: attempted to call increment on a histogram") + } } } #[inline(always)] fn inc_by(&self, value: i64) { match self { - Metric::C(c) => c.inc_by(value as u64), - Metric::G(g) => g.add(value), + Metric::IntCounter(c) => c.inc_by(value as u64), + Metric::IntGauge(g) => g.add(value), + Metric::FloatGauge(g) => { + warn!("attempted to increment a float gauge ('{}') by an integer - this is most likely a bug", self.fq_name()); + g.add(value as f64) + } + Metric::Histogram(_) => { + warn!("invalid operation: attempted to call increment on a histogram") + } } } #[inline(always)] fn set(&self, value: i64) { match self { - Metric::C(_c) => { + Metric::IntCounter(_c) => { warn!("Cannot set value for counter {:?}", self.fq_name()); } - Metric::G(g) => g.set(value), + Metric::IntGauge(g) => g.set(value), + Metric::FloatGauge(g) => { + warn!("attempted to set a float gauge ('{}') to an integer value - this is most likely a bug", self.fq_name()); + g.set(value as f64) + } + Metric::Histogram(_) => { + warn!("invalid operation: attempted to call set on a histogram") + } + } + } + + #[inline(always)] + fn set_float(&self, value: f64) { + match self { + Metric::IntCounter(_c) => { + warn!("Cannot set value for counter {:?}", self.fq_name()); + } + Metric::IntGauge(g) => { + warn!("attempted to set a integer gauge ('{}') to a float value - this is most likely a bug", self.fq_name()); + g.set(value as i64) + } + Metric::FloatGauge(g) => g.set(value), + Metric::Histogram(_) => { + warn!("invalid operation: attempted to call increment on a histogram") + } + } + } + + #[inline(always)] + fn add_histogram_observation(&self, value: f64) { + match self { + Metric::Histogram(h) => { + h.observe(value); + } + _ => warn!("attempted to add histogram observation on a non-histogram metric"), + } + } + + #[inline(always)] + fn start_timer(&self) -> Option { + match self { + Metric::Histogram(h) => Some(h.start_timer()), + _ => { + warn!("attempted to start histogram observation on a non-histogram metric"); + None + } } } } @@ -145,93 +371,165 @@ impl MetricsController { } } - pub fn set(&self, name: &str, value: i64) { + pub fn register_int_gauge<'a>(&self, name: &str, help: impl Into>) { + let Some(metric) = Metric::new_int_gauge(name, help.into().unwrap_or(name)) else { + return; + }; + self.register_metric(metric); + } + + pub fn register_float_gauge<'a>(&self, name: &str, help: impl Into>) { + let Some(metric) = Metric::new_float_gauge(name, help.into().unwrap_or(name)) else { + return; + }; + self.register_metric(metric); + } + + pub fn register_int_counter<'a>(&self, name: &str, help: impl Into>) { + let Some(metric) = Metric::new_int_counter(name, help.into().unwrap_or(name)) else { + return; + }; + self.register_metric(metric); + } + + pub fn register_histogram<'a>( + &self, + name: &str, + help: impl Into>, + buckets: Option<&[f64]>, + ) { + let Some(metric) = Metric::new_histogram(name, help.into().unwrap_or(name), buckets) else { + return; + }; + self.register_metric(metric); + } + + pub fn set(&self, name: &str, value: i64) -> bool { if let Some(metric) = self.registry_index.get(name) { metric.set(value); + true } else { - let gauge = match IntGauge::new(sanitize_metric_name(name), name) { - Ok(g) => g, - Err(e) => { - debug!("Failed to create gauge {:?}:\n{}", name, e); - return; - } - }; - self.register_gauge(Box::new(gauge)); - self.set(name, value) + false } } - pub fn inc(&self, name: &str) { + pub fn set_float(&self, name: &str, value: f64) -> bool { + if let Some(metric) = self.registry_index.get(name) { + metric.set_float(value); + true + } else { + false + } + } + + pub fn add_to_histogram(&self, name: &str, value: f64) -> bool { + if let Some(metric) = self.registry_index.get(name) { + metric.add_histogram_observation(value); + true + } else { + false + } + } + + pub fn start_timer(&self, name: &str) -> Option { + self.registry_index + .get(name) + .and_then(|metric| metric.start_timer()) + } + + pub fn inc(&self, name: &str) -> bool { if let Some(metric) = self.registry_index.get(name) { metric.inc(); + true } else { - let counter = match IntCounter::new(sanitize_metric_name(name), name) { - Ok(c) => c, - Err(e) => { - debug!("Failed to create counter {:?}:\n{}", name, e); - return; - } - }; - self.register_counter(Box::new(counter)); - self.inc(name) + false } } - pub fn inc_by(&self, name: &str, value: i64) { + pub fn inc_by(&self, name: &str, value: i64) -> bool { if let Some(metric) = self.registry_index.get(name) { metric.inc_by(value); + true } else { - let counter = match IntCounter::new(sanitize_metric_name(name), name) { - Ok(c) => c, - Err(e) => { - debug!("Failed to create counter {:?}:\n{}", name, e); - return; - } - }; - self.register_counter(Box::new(counter)); - self.inc_by(name, value) + false } } - fn register_gauge(&self, metric: Box) { - let fq_name = metric - .desc() - .first() - .map(|d| d.fq_name.clone()) - .unwrap_or_default(); + pub fn maybe_register_and_set<'a>( + &self, + name: &str, + value: i64, + help: impl Into>, + ) { + if !self.set(name, value) { + let help = help.into(); + self.register_int_gauge(name, help); + self.set(name, value); + } + } + + pub fn maybe_register_and_set_float<'a>( + &self, + name: &str, + value: f64, + help: impl Into>, + ) { + if !self.set_float(name, value) { + let help = help.into(); + self.register_float_gauge(name, help); + self.set_float(name, value); + } + } + + pub fn maybe_register_and_add_to_histogram<'a>( + &self, + name: &str, + value: f64, + buckets: Option<&[f64]>, + help: impl Into>, + ) { + if !self.add_to_histogram(name, value) { + let help = help.into(); + self.register_histogram(name, help, buckets); + self.add_to_histogram(name, value); + } + } + + pub fn maybe_register_and_inc<'a>(&self, name: &str, help: impl Into>) { + if !self.inc(name) { + let help = help.into(); + self.register_int_counter(name, help); + self.inc(name); + } + } + + pub fn maybe_register_and_inc_by<'a>( + &self, + name: &str, + value: i64, + help: impl Into>, + ) { + if !self.inc_by(name, value) { + let help = help.into(); + self.register_int_counter(name, help); + self.inc_by(name, value); + } + } + + pub fn register_metric(&self, metric: impl Into) { + let m = metric.into(); + let fq_name = m.fq_name(); if self.registry_index.contains_key(&fq_name) { return; } - match self.registry.register(metric.clone()) { + match self.registry.register(m.as_collector()) { Ok(_) => { - self.registry_index - .insert(fq_name, Metric::G(metric.clone())); + self.registry_index.insert(fq_name, m); } - Err(e) => { - debug!("Failed to register {:?}:\n{}", fq_name, e) - } - } - } - - fn register_counter(&self, metric: Box) { - let fq_name = metric - .desc() - .first() - .map(|d| d.fq_name.clone()) - .unwrap_or_default(); - - if self.registry_index.contains_key(&fq_name) { - return; - } - match self.registry.register(metric.clone()) { - Ok(_) => { - self.registry_index - .insert(fq_name, Metric::C(metric.clone())); - } - Err(e) => { - debug!("Failed to register {:?}:\n{}", fq_name, e) + Err(err) => { + debug!("Failed to register '{fq_name}': {err}") } } } @@ -275,4 +573,15 @@ mod tests { "packets_sent_34_242_65_133:1789" ) } + + #[test] + fn prepend_package_name() { + let literal = prepend_package_name!("foo"); + assert_eq!(literal, "nym_metrics_foo"); + + let bar = "bar"; + let format = format!("foomp_{}", bar); + let formatted = prepend_package_name!(format); + assert_eq!(formatted, "nym_metrics_foomp_bar"); + } } diff --git a/common/nym_offline_compact_ecash/src/scheme/aggregation.rs b/common/nym_offline_compact_ecash/src/scheme/aggregation.rs index 9018cde7ea..148a619a41 100644 --- a/common/nym_offline_compact_ecash/src/scheme/aggregation.rs +++ b/common/nym_offline_compact_ecash/src/scheme/aggregation.rs @@ -115,10 +115,7 @@ pub fn aggregate_signatures( let params = ecash_group_parameters(); // aggregate the signature - let signature = match Aggregatable::aggregate(signatures, indices) { - Ok(res) => res, - Err(err) => return Err(err), - }; + let signature = Aggregatable::aggregate(signatures, indices)?; // Ensure the aggregated signature is not an infinity point if bool::from(signature.is_at_infinity()) { diff --git a/common/nym_offline_compact_ecash/src/scheme/keygen.rs b/common/nym_offline_compact_ecash/src/scheme/keygen.rs index 406b368be0..8db1597e61 100644 --- a/common/nym_offline_compact_ecash/src/scheme/keygen.rs +++ b/common/nym_offline_compact_ecash/src/scheme/keygen.rs @@ -530,6 +530,15 @@ impl From for SecretKeyUser { } } +impl From 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 { diff --git a/common/nymsphinx/addressing/src/nodes.rs b/common/nymsphinx/addressing/src/nodes.rs index 0c1558c19c..c748a72dcd 100644 --- a/common/nymsphinx/addressing/src/nodes.rs +++ b/common/nymsphinx/addressing/src/nodes.rs @@ -1,12 +1,13 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -//! Encodoing and decoding node routing information. +//! 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}; diff --git a/common/nymsphinx/framing/src/packet.rs b/common/nymsphinx/framing/src/packet.rs index 7d6dfcfa01..fec9d5285f 100644 --- a/common/nymsphinx/framing/src/packet.rs +++ b/common/nymsphinx/framing/src/packet.rs @@ -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, }; diff --git a/common/nymsphinx/params/src/packet_version.rs b/common/nymsphinx/params/src/packet_version.rs index c8c6ce3fc6..8639dea499 100644 --- a/common/nymsphinx/params/src/packet_version.rs +++ b/common/nymsphinx/params/src/packet_version.rs @@ -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 { diff --git a/common/nyxd-scraper/src/storage/mod.rs b/common/nyxd-scraper/src/storage/mod.rs index 8ac0f07775..a3b0550677 100644 --- a/common/nyxd-scraper/src/storage/mod.rs +++ b/common/nyxd-scraper/src/storage/mod.rs @@ -13,7 +13,11 @@ use crate::{ models::{CommitSignature, Validator}, }, }; -use sqlx::{types::time::OffsetDateTime, ConnectOptions, Sqlite, Transaction}; +use sqlx::{ + sqlite::{SqliteAutoVacuum, SqliteSynchronous}, + types::time::OffsetDateTime, + ConnectOptions, Sqlite, Transaction, +}; use std::{fmt::Debug, path::Path}; use tendermint::{ block::{Commit, CommitSig}, @@ -51,6 +55,9 @@ impl ScraperStorage { #[instrument] pub async fn init + Debug>(database_path: P) -> Result { let opts = sqlx::sqlite::SqliteConnectOptions::new() + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) + .synchronous(SqliteSynchronous::Normal) + .auto_vacuum(SqliteAutoVacuum::Incremental) .filename(database_path) .create_if_missing(true) .disable_statement_logging(); diff --git a/common/service-provider-requests-common/src/lib.rs b/common/service-provider-requests-common/src/lib.rs index f9f0564e1d..3b294baba7 100644 --- a/common/service-provider-requests-common/src/lib.rs +++ b/common/service-provider-requests-common/src/lib.rs @@ -11,7 +11,7 @@ pub enum ServiceProviderType { Authenticator = 2, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Protocol { pub version: u8, pub service_provider_type: ServiceProviderType, diff --git a/common/statistics/Cargo.toml b/common/statistics/Cargo.toml index 9f8b8db177..230cf62a2f 100644 --- a/common/statistics/Cargo.toml +++ b/common/statistics/Cargo.toml @@ -26,3 +26,4 @@ nym-sphinx = { path = "../nymsphinx" } nym-credentials-interface = { path = "../credentials-interface" } nym-metrics = { path = "../nym-metrics" } nym-task = { path = "../task" } + diff --git a/common/statistics/src/clients/packet_statistics.rs b/common/statistics/src/clients/packet_statistics.rs index 5d6d1f9c1b..ba335d3627 100644 --- a/common/statistics/src/clients/packet_statistics.rs +++ b/common/statistics/src/clients/packet_statistics.rs @@ -428,7 +428,7 @@ impl PacketStatisticsControl { while self .history .front() - .map_or(false, |&(t, _)| t < recording_window) + .is_some_and(|&(t, _)| t < recording_window) { self.history.pop_front(); } @@ -462,7 +462,7 @@ impl PacketStatisticsControl { while self .rates .front() - .map_or(false, |&(t, _)| t < recording_window) + .is_some_and(|&(t, _)| t < recording_window) { self.rates.pop_front(); } diff --git a/common/statistics/src/gateways.rs b/common/statistics/src/gateways.rs index 398fda559a..4e8e701de1 100644 --- a/common/statistics/src/gateways.rs +++ b/common/statistics/src/gateways.rs @@ -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 @@ -86,4 +58,41 @@ pub enum SessionEvent { /// Address of the remote client opening the connection client: DestinationAddressBytes, }, + SessionDelete { + /// Address of the remote client opening the connection + 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, + } + } + + pub fn new_session_delete(client: DestinationAddressBytes) -> GatewaySessionEvent { + GatewaySessionEvent::SessionDelete { client } + } } diff --git a/common/statistics/src/lib.rs b/common/statistics/src/lib.rs index eccf217d77..c0ee03dddc 100644 --- a/common/statistics/src/lib.rs +++ b/common/statistics/src/lib.rs @@ -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>(prefix: &str, id_seed: M) -> String { let output = hasher.finalize(); format!("{:x}", output) } + +pub fn hash_identifier>(identifier: M) -> String { + format!("{:x}", Sha256::digest(identifier)) +} diff --git a/common/task/src/manager.rs b/common/task/src/manager.rs index b3bc6e0232..7d8ba3a780 100644 --- a/common/task/src/manager.rs +++ b/common/task/src/manager.rs @@ -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 { diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Account.ts b/common/types/bindings/ts-packages/types/src/types/rust/Account.ts new file mode 100644 index 0000000000..4980e9b69e --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Account.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CurrencyDenom } from "./CurrencyDenom"; + +export type Account = { client_address: string, base_mix_denom: string, display_mix_denom: CurrencyDenom, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/AccountEntry.ts b/common/types/bindings/ts-packages/types/src/types/rust/AccountEntry.ts new file mode 100644 index 0000000000..39db6db6cc --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/AccountEntry.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AccountEntry = { id: string, address: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/AccountWithMnemonic.ts b/common/types/bindings/ts-packages/types/src/types/rust/AccountWithMnemonic.ts new file mode 100644 index 0000000000..82d73ac020 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/AccountWithMnemonic.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Account } from "./Account"; + +export type AccountWithMnemonic = { account: Account, mnemonic: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/ActiveSetUpdate.ts b/common/types/bindings/ts-packages/types/src/types/rust/ActiveSetUpdate.ts new file mode 100644 index 0000000000..20e5640581 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/ActiveSetUpdate.ts @@ -0,0 +1,18 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Specification on how the active set should be updated. + */ +export type ActiveSetUpdate = { +/** + * The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`]) + */ +entry_gateways: number, +/** + * The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`]) + */ +exit_gateways: number, +/** + * The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`]. + */ +mixnodes: number, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Balance.ts b/common/types/bindings/ts-packages/types/src/types/rust/Balance.ts new file mode 100644 index 0000000000..b2632511e3 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Balance.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type Balance = { amount: DecCoin, printable_balance: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Coin.ts b/common/types/bindings/ts-packages/types/src/types/rust/Coin.ts new file mode 100644 index 0000000000..4dc9188584 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Coin.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Coin = { denom: string, amount: bigint, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/CosmosFee.ts b/common/types/bindings/ts-packages/types/src/types/rust/CosmosFee.ts new file mode 100644 index 0000000000..bcb0edc1aa --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/CosmosFee.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Coin } from "./Coin"; + +export type CosmosFee = { amount: Array, gas_limit: bigint, payer: string | null, granter: string | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/CurrencyDenom.ts b/common/types/bindings/ts-packages/types/src/types/rust/CurrencyDenom.ts new file mode 100644 index 0000000000..939585e625 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/CurrencyDenom.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CurrencyDenom = "unknown" | "nym" | "nymt" | "nyx" | "nyxt"; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/DecCoin.ts b/common/types/bindings/ts-packages/types/src/types/rust/DecCoin.ts new file mode 100644 index 0000000000..5b0a446ba8 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/DecCoin.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CurrencyDenom } from "./CurrencyDenom"; + +export type DecCoin = { denom: CurrencyDenom, amount: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Delegation.ts b/common/types/bindings/ts-packages/types/src/types/rust/Delegation.ts new file mode 100644 index 0000000000..44112fa3cf --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Delegation.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type Delegation = { owner: string, mix_id: number, amount: DecCoin, height: bigint, proxy: string | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/DelegationEvent.ts b/common/types/bindings/ts-packages/types/src/types/rust/DelegationEvent.ts new file mode 100644 index 0000000000..86a62f4c26 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/DelegationEvent.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { DelegationEventKind } from "./DelegationEventKind"; + +export type DelegationEvent = { kind: DelegationEventKind, mix_id: number, address: string, amount: DecCoin | null, proxy: string | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/DelegationEventKind.ts b/common/types/bindings/ts-packages/types/src/types/rust/DelegationEventKind.ts new file mode 100644 index 0000000000..ad43ee8282 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/DelegationEventKind.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type DelegationEventKind = "Delegate" | "Undelegate"; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/DelegationResult.ts b/common/types/bindings/ts-packages/types/src/types/rust/DelegationResult.ts new file mode 100644 index 0000000000..66cb790c18 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/DelegationResult.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type DelegationResult = { source_address: string, target_address: string, amount: DecCoin | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/DelegationSummaryResponse.ts b/common/types/bindings/ts-packages/types/src/types/rust/DelegationSummaryResponse.ts new file mode 100644 index 0000000000..113f6ac944 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/DelegationSummaryResponse.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { DelegationWithEverything } from "./DelegationWithEverything"; + +export type DelegationsSummaryResponse = { delegations: Array, total_delegations: DecCoin, total_rewards: DecCoin, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/DelegationWithEverything.ts b/common/types/bindings/ts-packages/types/src/types/rust/DelegationWithEverything.ts new file mode 100644 index 0000000000..ed8a84b8d7 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/DelegationWithEverything.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { DelegationEvent } from "./DelegationEvent"; +import type { NodeCostParams } from "./MixNodeCostParams"; + +export type DelegationWithEverything = { owner: string, mix_id: number, node_identity: string, amount: DecCoin, accumulated_by_delegates: DecCoin | null, accumulated_by_operator: DecCoin | null, block_height: bigint, delegated_on_iso_datetime: string | null, cost_params: NodeCostParams | null, avg_uptime_percent: number | null, stake_saturation: string | null, uses_vesting_contract_tokens: boolean, unclaimed_rewards: DecCoin | null, errors: string | null, pending_events: Array, mixnode_is_unbonding: boolean | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Fee.ts b/common/types/bindings/ts-packages/types/src/types/rust/Fee.ts new file mode 100644 index 0000000000..d88be5b4c4 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Fee.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CosmosFee } from "./CosmosFee"; + +export type Fee = { "Manual": CosmosFee } | { "Auto": number | null }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/FeeDetails.ts b/common/types/bindings/ts-packages/types/src/types/rust/FeeDetails.ts new file mode 100644 index 0000000000..7383077844 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/FeeDetails.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { Fee } from "./Fee"; + +export type FeeDetails = { amount: DecCoin | null, fee: Fee, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Gas.ts b/common/types/bindings/ts-packages/types/src/types/rust/Gas.ts new file mode 100644 index 0000000000..a76fdfecd9 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Gas.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Gas = { +/** + * units of gas used + */ +gas_units: bigint, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/GasInfo.ts b/common/types/bindings/ts-packages/types/src/types/rust/GasInfo.ts new file mode 100644 index 0000000000..b51eb3c02d --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/GasInfo.ts @@ -0,0 +1,12 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Gas } from "./Gas"; + +export type GasInfo = { +/** + * GasWanted is the maximum units of work we allow this tx to perform. + */ +gas_wanted: Gas, +/** + * GasUsed is the amount of gas actually consumed. + */ +gas_used: Gas, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Gateway.ts b/common/types/bindings/ts-packages/types/src/types/rust/Gateway.ts new file mode 100644 index 0000000000..7ecd1e6789 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Gateway.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Gateway = { host: string, mix_port: number, clients_port: number, location: string, sphinx_key: string, +/** + * Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients + */ +identity_key: string, version: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/GatewayBond.ts b/common/types/bindings/ts-packages/types/src/types/rust/GatewayBond.ts new file mode 100644 index 0000000000..1bbdf83099 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/GatewayBond.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { Gateway } from "./Gateway"; + +export type GatewayBond = { pledge_amount: DecCoin, owner: string, block_height: bigint, gateway: Gateway, proxy: string | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts b/common/types/bindings/ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts new file mode 100644 index 0000000000..7ed7f7907a --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts @@ -0,0 +1,35 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RewardedSetParams } from "./RewardedSetParams"; + +/** + * Specification on how the rewarding params should be updated. + */ +export type IntervalRewardingParamsUpdate = { +/** + * Defines the new value of the reward pool. + */ +reward_pool: string | null, +/** + * Defines the new value of the staking supply. + */ +staking_supply: string | null, +/** + * Defines the new value of the staking supply scale factor. + */ +staking_supply_scale_factor: string | null, +/** + * Defines the new value of the sybil resistance percent. + */ +sybil_resistance_percent: string | null, +/** + * Defines the new value of the active set work factor. + */ +active_set_work_factor: string | null, +/** + * Defines the new value of the interval pool emission rate. + */ +interval_pool_emission: string | null, +/** + * Defines the parameters of the rewarded set. + */ +rewarded_set_params: RewardedSetParams | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/MixNodeBond.ts b/common/types/bindings/ts-packages/types/src/types/rust/MixNodeBond.ts new file mode 100644 index 0000000000..730582750b --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/MixNodeBond.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { MixNode } from "./Mixnode"; + +export type MixNodeBond = { mix_id: number, owner: string, original_pledge: DecCoin, mix_node: MixNode, proxy: string | null, bonding_height: bigint, is_unbonding: boolean, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/MixNodeCostParams.ts b/common/types/bindings/ts-packages/types/src/types/rust/MixNodeCostParams.ts new file mode 100644 index 0000000000..8658f9c91e --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/MixNodeCostParams.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type NodeCostParams = { profit_margin_percent: string, interval_operating_cost: DecCoin, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/MixNodeDetails.ts b/common/types/bindings/ts-packages/types/src/types/rust/MixNodeDetails.ts new file mode 100644 index 0000000000..7103354d40 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/MixNodeDetails.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { MixNodeBond } from "./MixNodeBond"; +import type { NodeRewarding } from "./NodeRewarding"; + +export type MixNodeDetails = { bond_information: MixNodeBond, rewarding_details: NodeRewarding, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/Mixnode.ts b/common/types/bindings/ts-packages/types/src/types/rust/Mixnode.ts new file mode 100644 index 0000000000..ada169d2b4 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/Mixnode.ts @@ -0,0 +1,34 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Information provided by the node operator during bonding that are used to allow other entities to use the services of this node. + */ +export type MixNode = { +/** + * Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com + */ +host: string, +/** + * Port used by this mixnode for listening for mix packets. + */ +mix_port: number, +/** + * Port used by this mixnode for listening for verloc requests. + */ +verloc_port: number, +/** + * Port used by this mixnode for its http(s) API + */ +http_api_port: number, +/** + * Base58-encoded x25519 public key used for sphinx key derivation. + */ +sphinx_key: string, +/** + * Base58-encoded ed25519 EdDSA public key. + */ +identity_key: string, +/** + * The self-reported semver version of this mixnode. + */ +version: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/NodeRewarding.ts b/common/types/bindings/ts-packages/types/src/types/rust/NodeRewarding.ts new file mode 100644 index 0000000000..1d472fbfcb --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/NodeRewarding.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NodeCostParams } from "./MixNodeCostParams"; + +export type NodeRewarding = { cost_params: NodeCostParams, operator: string, delegates: string, total_unit_reward: string, unit_delegation: string, last_rewarded_epoch: number, unique_delegations: number, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/NymNodeBond.ts b/common/types/bindings/ts-packages/types/src/types/rust/NymNodeBond.ts new file mode 100644 index 0000000000..0d50d01f55 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/NymNodeBond.ts @@ -0,0 +1,39 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type NymNodeBond = { +/** + * Unique id assigned to the bonded node. + */ +node_id: number, +/** + * Address of the owner of this nym-node. + */ +owner: string, +/** + * Original amount pledged by the operator of this node. + */ +original_pledge: DecCoin, +/** + * Block height at which this nym-node has been bonded. + */ +bonding_height: bigint, +/** + * Flag to indicate whether this node is in the process of unbonding, + * that will conclude upon the epoch finishing. + */ +is_unbonding: boolean, +/** + * Network address of this nym-node, for example 1.1.1.1 or foo.mixnode.com + * that is used to discover other capabilities of this node. + */ +host: string, +/** + * Allow specifying custom port for accessing the http, and thus self-described, api + * of this node for the capabilities discovery. + */ +custom_http_port: number | null, +/** + * Base58-encoded ed25519 EdDSA public key. + */ +identity_key: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/NymNodeDetails.ts b/common/types/bindings/ts-packages/types/src/types/rust/NymNodeDetails.ts new file mode 100644 index 0000000000..3baee09908 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/NymNodeDetails.ts @@ -0,0 +1,21 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NodeRewarding } from "./NodeRewarding"; +import type { NymNodeBond } from "./NymNodeBond"; +import type { PendingNodeChanges } from "./PendingNodeChanges"; + +/** + * Full details associated with given node. + */ +export type NymNodeDetails = { +/** + * Basic bond information of this node, such as owner address, original pledge, etc. + */ +bond_information: NymNodeBond, +/** + * Details used for computation of rewarding related data. + */ +rewarding_details: NodeRewarding, +/** + * Adjustments to the node that are scheduled to happen during future epoch/interval transitions. + */ +pending_changes: PendingNodeChanges, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/OriginalVestingResponse.ts b/common/types/bindings/ts-packages/types/src/types/rust/OriginalVestingResponse.ts new file mode 100644 index 0000000000..33d307ba18 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/OriginalVestingResponse.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type OriginalVestingResponse = { amount: DecCoin, number_of_periods: number, period_duration: bigint, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/PendingEpochEvent.ts b/common/types/bindings/ts-packages/types/src/types/rust/PendingEpochEvent.ts new file mode 100644 index 0000000000..9df4241249 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/PendingEpochEvent.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PendingEpochEventData } from "./PendingEpochEventData"; + +export type PendingEpochEvent = { id: number, created_at: bigint, event: PendingEpochEventData, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/PendingEpochEventData.ts b/common/types/bindings/ts-packages/types/src/types/rust/PendingEpochEventData.ts new file mode 100644 index 0000000000..8ce22bacf9 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/PendingEpochEventData.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActiveSetUpdate } from "./ActiveSetUpdate"; +import type { DecCoin } from "./DecCoin"; + +export type PendingEpochEventData = { "Delegate": { owner: string, mix_id: number, amount: DecCoin, proxy: string | null, } } | { "Undelegate": { owner: string, mix_id: number, proxy: string | null, } } | { "PledgeMore": { mix_id: number, amount: DecCoin, } } | { "DecreasePledge": { mix_id: number, decrease_by: DecCoin, } } | { "UnbondMixnode": { mix_id: number, } } | { "UpdateActiveSetSize": { new_size: number, } } | { "NymNodePledgeMore": { node_id: number, amount: DecCoin, } } | { "NymNodeDecreasePledge": { node_id: number, decrease_by: DecCoin, } } | { "UnbondNymNode": { node_id: number, } } | { "UpdateActiveSet": { update: ActiveSetUpdate, } }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/PendingIntervalEvent.ts b/common/types/bindings/ts-packages/types/src/types/rust/PendingIntervalEvent.ts new file mode 100644 index 0000000000..5358a2a823 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/PendingIntervalEvent.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PendingIntervalEventData } from "./PendingIntervalEventData"; + +export type PendingIntervalEvent = { id: number, created_at: bigint, event: PendingIntervalEventData, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/PendingIntervalEventData.ts b/common/types/bindings/ts-packages/types/src/types/rust/PendingIntervalEventData.ts new file mode 100644 index 0000000000..788ea37be8 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/PendingIntervalEventData.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { IntervalRewardingParamsUpdate } from "./IntervalRewardingParamsUpdate"; +import type { NodeCostParams } from "./MixNodeCostParams"; + +export type PendingIntervalEventData = { "ChangeMixCostParams": { mix_id: number, new_costs: NodeCostParams, } } | { "ChangeNymNodeCostParams": { node_id: number, new_costs: NodeCostParams, } } | { "UpdateRewardingParams": { update: IntervalRewardingParamsUpdate, } } | { "UpdateIntervalConfig": { epochs_in_interval: number, epoch_duration_secs: bigint, } }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/PendingNodeChanges.ts b/common/types/bindings/ts-packages/types/src/types/rust/PendingNodeChanges.ts new file mode 100644 index 0000000000..b1667a190e --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/PendingNodeChanges.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PendingNodeChanges = { pledge_change: number | null, cost_params_change: number | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/PledgeData.ts b/common/types/bindings/ts-packages/types/src/types/rust/PledgeData.ts new file mode 100644 index 0000000000..3accba8d63 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/PledgeData.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type PledgeData = { amount: DecCoin, block_time: bigint, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/RewardedSetParams.ts b/common/types/bindings/ts-packages/types/src/types/rust/RewardedSetParams.ts new file mode 100644 index 0000000000..e1d09ac0e3 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/RewardedSetParams.ts @@ -0,0 +1,19 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RewardedSetParams = { +/** + * The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`]) + */ +entry_gateways: number, +/** + * The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`]) + */ +exit_gateways: number, +/** + * The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`]. + */ +mixnodes: number, +/** + * Number of nodes in the 'standby' set. (i.e. [`Role::Standby`]) + */ +standby: number, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/RpcTransactionResponse.ts b/common/types/bindings/ts-packages/types/src/types/rust/RpcTransactionResponse.ts new file mode 100644 index 0000000000..6067e083ca --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/RpcTransactionResponse.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { Gas } from "./Gas"; + +export type RpcTransactionResponse = { index: number, tx_result_json: string, block_height: bigint, transaction_hash: string, gas_used: Gas, gas_wanted: Gas, fee: DecCoin | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/SendTxResult.ts b/common/types/bindings/ts-packages/types/src/types/rust/SendTxResult.ts new file mode 100644 index 0000000000..cb39f47184 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/SendTxResult.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { Gas } from "./Gas"; +import type { TransactionDetails } from "./TransactionDetails"; + +export type SendTxResult = { block_height: bigint, code: number, details: TransactionDetails, gas_used: Gas, gas_wanted: Gas, tx_hash: string, fee: DecCoin | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/TransactionDetails.ts b/common/types/bindings/ts-packages/types/src/types/rust/TransactionDetails.ts new file mode 100644 index 0000000000..2bcef1a14d --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/TransactionDetails.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; + +export type TransactionDetails = { amount: DecCoin, from_address: string, to_address: string, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/TransactionExecuteResult.ts b/common/types/bindings/ts-packages/types/src/types/rust/TransactionExecuteResult.ts new file mode 100644 index 0000000000..5e5e902f90 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/TransactionExecuteResult.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { GasInfo } from "./GasInfo"; + +export type TransactionExecuteResult = { logs_json: string, msg_responses_json: string, transaction_hash: string, gas_info: GasInfo, fee: DecCoin | null, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/VestingAccountInfo.ts b/common/types/bindings/ts-packages/types/src/types/rust/VestingAccountInfo.ts new file mode 100644 index 0000000000..5602f9a381 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/VestingAccountInfo.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DecCoin } from "./DecCoin"; +import type { VestingPeriod } from "./VestingPeriod"; + +export type VestingAccountInfo = { owner_address: string, staking_address: string | null, start_time: bigint, periods: Array, amount: DecCoin, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/VestingPeriod.ts b/common/types/bindings/ts-packages/types/src/types/rust/VestingPeriod.ts new file mode 100644 index 0000000000..242962750f --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/VestingPeriod.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type VestingPeriod = { start_time: bigint, period_seconds: bigint, }; diff --git a/common/types/bindings/ts-packages/types/src/types/rust/WrappedDelegationEvent.ts b/common/types/bindings/ts-packages/types/src/types/rust/WrappedDelegationEvent.ts new file mode 100644 index 0000000000..854982c359 --- /dev/null +++ b/common/types/bindings/ts-packages/types/src/types/rust/WrappedDelegationEvent.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DelegationEvent } from "./DelegationEvent"; + +export type WrappedDelegationEvent = { event: DelegationEvent, node_identity: string, }; diff --git a/common/verloc/Cargo.toml b/common/verloc/Cargo.toml new file mode 100644 index 0000000000..e906f72dde --- /dev/null +++ b/common/verloc/Cargo.toml @@ -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" } diff --git a/common/verloc/src/error.rs b/common/verloc/src/error.rs new file mode 100644 index 0000000000..836177d62f --- /dev/null +++ b/common/verloc/src/error.rs @@ -0,0 +1,72 @@ +// Copyright 2021 - Nym Technologies SA +// 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, +} diff --git a/common/verloc/src/lib.rs b/common/verloc/src/lib.rs new file mode 100644 index 0000000000..2f976e06fb --- /dev/null +++ b/common/verloc/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright 2024 - Nym Technologies SA +// 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; diff --git a/common/verloc/src/measurements/config.rs b/common/verloc/src/measurements/config.rs new file mode 100644 index 0000000000..d6a15ef3d2 --- /dev/null +++ b/common/verloc/src/measurements/config.rs @@ -0,0 +1,135 @@ +// Copyright 2024 - Nym Technologies SA +// 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, + + /// User agent used for querying the nym-api + pub user_agent: UserAgent, +} + +impl Config { + pub fn build(nym_api_urls: Vec, user_agent: impl Into) -> ConfigBuilder { + ConfigBuilder::new(nym_api_urls, user_agent) + } +} + +#[must_use] +pub struct ConfigBuilder(Config); + +impl ConfigBuilder { + pub fn new(nym_api_urls: Vec, user_agent: impl Into) -> 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) -> 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 + } +} diff --git a/common/mixnode-common/src/verloc/listener.rs b/common/verloc/src/measurements/listener.rs similarity index 78% rename from common/mixnode-common/src/verloc/listener.rs rename to common/verloc/src/measurements/listener.rs index aa58b56b8e..77c1739068 100644 --- a/common/mixnode-common/src/verloc/listener.rs +++ b/common/verloc/src/measurements/listener.rs @@ -1,29 +1,29 @@ // Copyright 2021 - Nym Technologies SA // 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, shutdown: TaskClient, } impl PacketListener { - pub(crate) fn new( + pub fn new( address: SocketAddr, identity: Arc, shutdown: TaskClient, @@ -37,13 +37,13 @@ impl PacketListener { } impl PacketListener { - pub(super) async fn run(self: Arc) { + pub async fn run(self: Arc) { 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), } -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 for EchoPacketCodecError { - fn from(err: io::Error) -> Self { - EchoPacketCodecError::IoError(err) +impl From 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); diff --git a/common/verloc/src/measurements/measurer.rs b/common/verloc/src/measurements/measurer.rs new file mode 100644 index 0000000000..e56f35d086 --- /dev/null +++ b/common/verloc/src/measurements/measurer.rs @@ -0,0 +1,225 @@ +// Copyright 2024 - Nym Technologies SA +// 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, + packet_listener: Arc, + shutdown_listener: TaskClient, + state: SharedVerlocStats, +} + +impl VerlocMeasurer { + pub fn new( + config: Config, + identity: Arc, + 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) -> 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::>(); + + // 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> { + 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::>(); + + // 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, +} diff --git a/common/verloc/src/measurements/metrics.rs b/common/verloc/src/measurements/metrics.rs new file mode 100644 index 0000000000..210607696e --- /dev/null +++ b/common/verloc/src/measurements/metrics.rs @@ -0,0 +1,50 @@ +// Copyright 2024 - Nym Technologies SA +// 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>, +} + +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) { + 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 + } +} diff --git a/common/verloc/src/measurements/mod.rs b/common/verloc/src/measurements/mod.rs new file mode 100644 index 0000000000..303fab2240 --- /dev/null +++ b/common/verloc/src/measurements/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// 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; diff --git a/common/mixnode-common/src/verloc/packet.rs b/common/verloc/src/measurements/packet.rs similarity index 61% rename from common/mixnode-common/src/verloc/packet.rs rename to common/verloc/src/measurements/packet.rs index c5729904b7..2a918b007a 100644 --- a/common/mixnode-common/src/verloc/packet.rs +++ b/common/verloc/src/measurements/packet.rs @@ -1,20 +1,20 @@ // Copyright 2021 - Nym Technologies SA // 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 { + pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result { 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 { + remote_ed25519: &ed25519::PublicKey, + ) -> Result { 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, diff --git a/common/mixnode-common/src/verloc/sender.rs b/common/verloc/src/measurements/sender.rs similarity index 68% rename from common/mixnode-common/src/verloc/sender.rs rename to common/verloc/src/measurements/sender.rs index 5887a22a6e..292cd8d8c0 100644 --- a/common/mixnode-common/src/verloc/sender.rs +++ b/common/verloc/src/measurements/sender.rs @@ -1,11 +1,10 @@ // Copyright 2021-2024 - Nym Technologies SA // 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, +pub struct PacketSender { + identity: Arc, // 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, + pub fn new( + identity: Arc, 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, tested_node: TestedNode, - ) -> Result { + ) -> Result { 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); diff --git a/common/verloc/src/models.rs b/common/verloc/src/models.rs new file mode 100644 index 0000000000..0bedab812c --- /dev/null +++ b/common/verloc/src/models.rs @@ -0,0 +1,225 @@ +// Copyright 2024 - Nym Technologies SA +// 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, + + pub results: Vec, +} + +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, +} + +impl VerlocNodeResult { + pub fn new( + node_identity: ed25519::PublicKey, + latest_measurement: Option, + ) -> Self { + VerlocNodeResult { + node_identity, + latest_measurement, + } + } +} + +impl PartialOrd for VerlocNodeResult { + fn partial_cmp(&self, other: &Self) -> Option { + 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::(); + 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::() + / 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 { + 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); + } +} diff --git a/common/wasm/client-core/src/helpers.rs b/common/wasm/client-core/src/helpers.rs index eee064e2cc..04d6960377 100644 --- a/common/wasm/client-core/src/helpers.rs +++ b/common/wasm/client-core/src/helpers.rs @@ -121,9 +121,10 @@ pub async fn setup_gateway_from_api( force_tls: bool, chosen_gateway: Option, nym_apis: &[Url], + minimum_performance: u8, ) -> Result { let mut rng = thread_rng(); - let gateways = current_gateways(&mut rng, nym_apis, None).await?; + let gateways = current_gateways(&mut rng, nym_apis, None, minimum_performance).await?; setup_gateway_wasm(client_store, force_tls, chosen_gateway, &gateways).await } diff --git a/common/wireguard/Cargo.toml b/common/wireguard/Cargo.toml index c594e6b891..c999861cd6 100644 --- a/common/wireguard/Cargo.toml +++ b/common/wireguard/Cargo.toml @@ -27,6 +27,7 @@ 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" } @@ -35,3 +36,4 @@ nym-gateway-storage = { path = "../gateway-storage" } nym-network-defaults = { path = "../network-defaults" } nym-task = { path = "../task" } nym-wireguard-types = { path = "../wireguard-types" } +nym-node-metrics = { path = "../../nym-node/nym-node-metrics" } diff --git a/common/wireguard/src/error.rs b/common/wireguard/src/error.rs index 65a7269952..d2fd0e7956 100644 --- a/common/wireguard/src/error.rs +++ b/common/wireguard/src/error.rs @@ -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), diff --git a/common/wireguard/src/lib.rs b/common/wireguard/src/lib.rs index 48f3c1f8ed..f397a3bbea 100644 --- a/common/wireguard/src/lib.rs +++ b/common/wireguard/src/lib.rs @@ -7,16 +7,18 @@ // #![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; @@ -81,8 +83,9 @@ pub struct WireguardData { /// Start wireguard device #[cfg(target_os = "linux")] -pub async fn start_wireguard( - storage: St, +pub async fn start_wireguard( + storage: nym_gateway_storage::GatewayStorage, + metrics: nym_node_metrics::NymNodeMetrics, all_peers: Vec, task_client: nym_task::TaskClient, wireguard_data: WireguardData, @@ -93,6 +96,7 @@ pub async fn start_wireguard 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)?; @@ -121,6 +125,7 @@ pub async fn start_wireguard .await?; peer_bandwidth_managers.insert(peer.public_key.clone(), (bandwidth_manager, peer.clone())); } + wg_api.create_interface()?; let interface_config = InterfaceConfiguration { name: ifname.clone(), @@ -130,6 +135,11 @@ pub async fn start_wireguard 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([ @@ -166,6 +176,7 @@ pub async fn start_wireguard let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api)); let mut controller = PeerController::new( storage, + metrics, wg_api.clone(), host, peer_bandwidth_managers, diff --git a/common/wireguard/src/peer_controller.rs b/common/wireguard/src/peer_controller.rs index 321b15462f..19555fd37e 100644 --- a/common/wireguard/src/peer_controller.rs +++ b/common/wireguard/src/peer_controller.rs @@ -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,8 +15,10 @@ use nym_credential_verification::{ bandwidth_storage_manager::BandwidthStorageManager, BandwidthFlushingBehaviourConfig, ClientBandwidth, }; -use nym_gateway_storage::Storage; +use nym_gateway_storage::GatewayStorage; +use nym_node_metrics::NymNodeMetrics; use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK; +use std::time::{Duration, SystemTime}; use std::{collections::HashMap, sync::Arc}; use tokio::sync::{mpsc, RwLock}; use tokio_stream::{wrappers::IntervalStream, StreamExt}; @@ -62,24 +65,31 @@ pub struct QueryBandwidthControlResponse { pub bandwidth_data: Option, } -pub struct PeerController { - storage: St, +pub struct PeerController { + storage: GatewayStorage, + + // we have "all" metrics of a node, but they're behind a single Arc pointer, + // so the overhead is minimal + metrics: NymNodeMetrics, + // used to receive commands from individual handles too request_tx: mpsc::Sender, request_rx: mpsc::Receiver, wg_api: Arc, host_information: Arc>, - bw_storage_managers: HashMap>>, + bw_storage_managers: HashMap>, timeout_check_interval: IntervalStream, task_client: nym_task::TaskClient, } -impl PeerController { +impl PeerController { + #[allow(clippy::too_many_arguments)] pub fn new( - storage: St, + storage: GatewayStorage, + metrics: NymNodeMetrics, wg_api: Arc, initial_host_information: Host, - bw_storage_managers: HashMap>, Peer)>, + bw_storage_managers: HashMap, Peer)>, request_tx: mpsc::Sender, request_rx: mpsc::Receiver, task_client: nym_task::TaskClient, @@ -122,6 +132,7 @@ impl PeerController { request_rx, timeout_check_interval, task_client, + metrics, } } @@ -158,9 +169,9 @@ impl PeerController { } pub async fn generate_bandwidth_manager( - storage: St, + storage: GatewayStorage, public_key: &Key, - ) -> Result>, Error> { + ) -> Result, Error> { if let Some(client_id) = storage .get_wireguard_peer(&public_key.to_string()) .await? @@ -256,7 +267,57 @@ impl PeerController { })) } + async fn update_metrics(&self, new_host: &Host) { + let now = SystemTime::now(); + const ACTIVITY_THRESHOLD: Duration = Duration::from_secs(60); + + let old_host = self.host_information.read().await; + + let total_peers = new_host.peers.len(); + let mut active_peers = 0; + let mut new_rx = 0; + let mut new_tx = 0; + + for (peer_key, peer) in new_host.peers.iter() { + // only consider pre-existing peers, + // so that the value would always be increasing + if let Some(prior) = old_host.peers.get(peer_key) { + let delta_rx = peer.rx_bytes.saturating_sub(prior.rx_bytes); + let delta_tx = prior.tx_bytes.saturating_sub(prior.tx_bytes); + + new_rx += delta_rx; + new_tx += delta_tx; + } + + // if a peer hasn't performed a handshake in last minute, + // I think it's reasonable to assume it's no longer active + let Some(last_handshake) = peer.last_handshake else { + continue; + }; + let Ok(elapsed) = now.duration_since(last_handshake) else { + continue; + }; + if elapsed < ACTIVITY_THRESHOLD { + active_peers += 1; + } + } + + self.metrics.wireguard.update( + // if the conversion fails it means we're running not running on a 64bit system + // and that's a reason enough for this failure. + new_rx.try_into().expect( + "failed to convert bytes from u64 to usize - are you running on non 64bit system?", + ), + new_tx.try_into().expect( + "failed to convert bytes from u64 to usize - are you running on non 64bit system?", + ), + total_peers, + active_peers, + ); + } + pub async fn run(&mut self) { + info!("started wireguard peer controller"); loop { tokio::select! { _ = self.timeout_check_interval.next() => { @@ -264,6 +325,8 @@ impl PeerController { log::error!("Can't read wireguard kernel data"); continue; }; + self.update_metrics(&host).await; + *self.host_information.write().await = host; } _ = self.task_client.recv() => { diff --git a/common/wireguard/src/peer_handle.rs b/common/wireguard/src/peer_handle.rs index a6e5f9e502..c7c8709bbc 100644 --- a/common/wireguard/src/peer_handle.rs +++ b/common/wireguard/src/peer_handle.rs @@ -10,7 +10,6 @@ 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; @@ -18,26 +17,26 @@ use std::time::{Duration, SystemTime}; use tokio::sync::{mpsc, RwLock}; use tokio_stream::{wrappers::IntervalStream, StreamExt}; -pub(crate) type SharedBandwidthStorageManager = Arc>>; +pub(crate) type SharedBandwidthStorageManager = Arc>; const AUTO_REMOVE_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 30); // 30 days -pub struct PeerHandle { +pub struct PeerHandle { public_key: Key, host_information: Arc>, - peer_storage_manager: PeerStorageManager, - bandwidth_storage_manager: Option>, + peer_storage_manager: PeerStorageManager, + bandwidth_storage_manager: Option, request_tx: mpsc::Sender, timeout_check_interval: IntervalStream, task_client: TaskClient, startup_timestamp: SystemTime, } -impl PeerHandle { +impl PeerHandle { pub fn new( public_key: Key, host_information: Arc>, - peer_storage_manager: PeerStorageManager, - bandwidth_storage_manager: Option>, + peer_storage_manager: PeerStorageManager, + bandwidth_storage_manager: Option, request_tx: mpsc::Sender, task_client: &TaskClient, ) -> Self { diff --git a/common/wireguard/src/peer_storage_manager.rs b/common/wireguard/src/peer_storage_manager.rs index f7992b8bb0..c2567ce83a 100644 --- a/common/wireguard/src/peer_storage_manager.rs +++ b/common/wireguard/src/peer_storage_manager.rs @@ -4,7 +4,7 @@ use crate::error::Error; use defguard_wireguard_rs::host::Peer; use nym_gateway_storage::models::WireguardPeer; -use nym_gateway_storage::Storage; +use nym_gateway_storage::GatewayStorage; use std::time::Duration; use time::OffsetDateTime; @@ -29,15 +29,15 @@ impl Default for PeerFlushingBehaviourConfig { } } -pub struct PeerStorageManager { - pub(crate) storage: S, +pub struct PeerStorageManager { + pub(crate) storage: GatewayStorage, pub(crate) peer_information: Option, pub(crate) cfg: PeerFlushingBehaviourConfig, pub(crate) with_client_id: bool, } -impl PeerStorageManager { - pub(crate) fn new(storage: S, peer: Peer, with_client_id: bool) -> Self { +impl PeerStorageManager { + pub(crate) fn new(storage: GatewayStorage, peer: Peer, with_client_id: bool) -> Self { let peer_information = Some(PeerInformation::new(peer)); Self { storage, diff --git a/contracts/mixnet-vesting-integration-tests/src/support/setup.rs b/contracts/mixnet-vesting-integration-tests/src/support/setup.rs index 7193097aa4..f1d0328f69 100644 --- a/contracts/mixnet-vesting-integration-tests/src/support/setup.rs +++ b/contracts/mixnet-vesting-integration-tests/src/support/setup.rs @@ -464,10 +464,7 @@ pub fn instantiate_contracts( mixnet_contract_address.clone(), &nym_mixnet_contract_common::MigrateMsg { vesting_contract_address: Some(vesting_contract_address.to_string()), - current_nym_node_semver: "1.1.10".to_string(), - version_score_weights: Default::default(), unsafe_skip_state_updates: Some(true), - version_score_params: Default::default(), }, mixnet_code_id, ) diff --git a/contracts/mixnet/schema/nym-mixnet-contract.json b/contracts/mixnet/schema/nym-mixnet-contract.json index 3549011058..2cf7b7675f 100644 --- a/contracts/mixnet/schema/nym-mixnet-contract.json +++ b/contracts/mixnet/schema/nym-mixnet-contract.json @@ -3470,43 +3470,13 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MigrateMsg", "type": "object", - "required": [ - "current_nym_node_semver" - ], "properties": { - "current_nym_node_semver": { - "type": "string" - }, "unsafe_skip_state_updates": { "type": [ "boolean", "null" ] }, - "version_score_params": { - "default": { - "penalty": "0.995", - "penalty_scaling": "1.65" - }, - "allOf": [ - { - "$ref": "#/definitions/VersionScoreFormulaParams" - } - ] - }, - "version_score_weights": { - "default": { - "major": 100, - "minor": 10, - "patch": 1, - "prerelease": 1 - }, - "allOf": [ - { - "$ref": "#/definitions/OutdatedVersionWeights" - } - ] - }, "vesting_contract_address": { "type": [ "string", @@ -3514,63 +3484,7 @@ ] } }, - "additionalProperties": false, - "definitions": { - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "OutdatedVersionWeights": { - "description": "Defines weights for calculating numbers of versions behind the current release.", - "type": "object", - "required": [ - "major", - "minor", - "patch", - "prerelease" - ], - "properties": { - "major": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "minor": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "patch": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "prerelease": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "VersionScoreFormulaParams": { - "description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters", - "type": "object", - "required": [ - "penalty", - "penalty_scaling" - ], - "properties": { - "penalty": { - "$ref": "#/definitions/Decimal" - }, - "penalty_scaling": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - } - } + "additionalProperties": false }, "sudo": null, "responses": { diff --git a/contracts/mixnet/schema/raw/migrate.json b/contracts/mixnet/schema/raw/migrate.json index 3844bce308..57f0d2acdb 100644 --- a/contracts/mixnet/schema/raw/migrate.json +++ b/contracts/mixnet/schema/raw/migrate.json @@ -2,43 +2,13 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MigrateMsg", "type": "object", - "required": [ - "current_nym_node_semver" - ], "properties": { - "current_nym_node_semver": { - "type": "string" - }, "unsafe_skip_state_updates": { "type": [ "boolean", "null" ] }, - "version_score_params": { - "default": { - "penalty": "0.995", - "penalty_scaling": "1.65" - }, - "allOf": [ - { - "$ref": "#/definitions/VersionScoreFormulaParams" - } - ] - }, - "version_score_weights": { - "default": { - "major": 100, - "minor": 10, - "patch": 1, - "prerelease": 1 - }, - "allOf": [ - { - "$ref": "#/definitions/OutdatedVersionWeights" - } - ] - }, "vesting_contract_address": { "type": [ "string", @@ -46,61 +16,5 @@ ] } }, - "additionalProperties": false, - "definitions": { - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "OutdatedVersionWeights": { - "description": "Defines weights for calculating numbers of versions behind the current release.", - "type": "object", - "required": [ - "major", - "minor", - "patch", - "prerelease" - ], - "properties": { - "major": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "minor": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "patch": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "prerelease": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "VersionScoreFormulaParams": { - "description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters", - "type": "object", - "required": [ - "penalty", - "penalty_scaling" - ], - "properties": { - "penalty": { - "$ref": "#/definitions/Decimal" - }, - "penalty_scaling": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - } - } + "additionalProperties": false } diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 3526e58636..60dd9e4fe3 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -603,7 +603,7 @@ pub fn query( #[entry_point] pub fn migrate( mut deps: DepsMut<'_>, - env: Env, + _env: Env, msg: MigrateMsg, ) -> Result { set_build_information!(deps.storage)?; @@ -612,7 +612,7 @@ pub fn migrate( let skip_state_updates = msg.unsafe_skip_state_updates.unwrap_or(false); if !skip_state_updates { - crate::queued_migrations::add_config_score_params(deps.branch(), env, &msg)?; + crate::queued_migrations::restore_node_version_history(deps.branch())?; } // due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address diff --git a/contracts/mixnet/src/mixnet_contract_settings/storage.rs b/contracts/mixnet/src/mixnet_contract_settings/storage.rs index 6201972e51..992d15ab8a 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/storage.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/storage.rs @@ -34,9 +34,13 @@ impl NymNodeVersionHistory<'_> { } fn next_id(&self, storage: &mut dyn Storage) -> Result { - let next = self.id_counter.may_load(storage)?.unwrap_or_default(); - self.id_counter.save(storage, &next)?; - Ok(next) + let id = self + .id_counter + .may_load(storage)? + .map(|current| current + 1) + .unwrap_or_default(); + self.id_counter.save(storage, &id)?; + Ok(id) } pub fn current_version( @@ -56,10 +60,10 @@ impl NymNodeVersionHistory<'_> { pub fn insert_new( &self, storage: &mut dyn Storage, - entry: HistoricalNymNodeVersion, + entry: &HistoricalNymNodeVersion, ) -> Result { let next_id = self.next_id(storage)?; - self.version_history.save(storage, next_id, &entry)?; + self.version_history.save(storage, next_id, entry)?; Ok(next_id) } @@ -79,7 +83,7 @@ impl NymNodeVersionHistory<'_> { // treat this as genesis let genesis = HistoricalNymNodeVersion::genesis(raw_semver.to_string(), env.block.height); - return self.insert_new(storage, genesis); + return self.insert_new(storage, &genesis); }; let current_semver = current.version_information.semver_unchecked(); @@ -99,7 +103,7 @@ impl NymNodeVersionHistory<'_> { introduced_at_height: env.block.height, difference_since_genesis: diff, }; - self.insert_new(storage, entry) + self.insert_new(storage, &entry) } } @@ -170,3 +174,218 @@ pub(crate) fn initialise_storage( ADMIN.set(deps, Some(initial_admin))?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(test)] + mod nym_node_version_history { + use super::*; + use crate::support::tests::test_helpers::TestSetup; + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + + #[test] + fn getting_current() -> anyhow::Result<()> { + // empty storage + let deps = mock_dependencies(); + let storage = NymNodeVersionHistory::new(); + assert!(storage.current_version(&deps.storage)?.is_none()); + + let mut test = TestSetup::new(); + + let zeroth = storage.current_version(test.storage())?.unwrap(); + let manual_zeroth = storage.version_history.load(test.storage(), 0)?; + assert_eq!(zeroth.version_information, manual_zeroth); + + // manually update the counter to make sure data is still read correctly + let dummy = HistoricalNymNodeVersion { + semver: "1.2.3".to_string(), + introduced_at_height: 1234, + difference_since_genesis: Default::default(), + }; + storage.id_counter.save(test.storage_mut(), &123)?; + storage + .version_history + .save(test.storage_mut(), 123, &dummy)?; + + let updated = storage.current_version(test.storage())?.unwrap(); + assert_eq!(updated.version_information, dummy); + + Ok(()) + } + + #[test] + fn inserting_new_entry() -> anyhow::Result<()> { + let mut test = TestSetup::new(); + let storage = NymNodeVersionHistory::new(); + + let first = HistoricalNymNodeVersion { + semver: "1.1.1".to_string(), + introduced_at_height: 12, + difference_since_genesis: Default::default(), + }; + let second = HistoricalNymNodeVersion { + semver: "1.1.2".to_string(), + introduced_at_height: 123, + difference_since_genesis: Default::default(), + }; + let third = HistoricalNymNodeVersion { + semver: "1.1.3".to_string(), + introduced_at_height: 1234, + difference_since_genesis: Default::default(), + }; + + assert_eq!(storage.id_counter.load(test.storage())?, 0); + + // id is correctly incremented for each case and no entry is overwritten + storage.insert_new(test.storage_mut(), &first)?; + assert_eq!(storage.id_counter.load(test.storage())?, 1); + + storage.insert_new(test.storage_mut(), &second)?; + assert_eq!(storage.id_counter.load(test.storage())?, 2); + + storage.insert_new(test.storage_mut(), &third)?; + assert_eq!(storage.id_counter.load(test.storage())?, 3); + + assert_eq!(storage.version_history.load(test.storage(), 1)?, first); + assert_eq!(storage.version_history.load(test.storage(), 2)?, second); + assert_eq!(storage.version_history.load(test.storage(), 3)?, third); + + Ok(()) + } + + #[test] + fn inserting_initial_semver() -> anyhow::Result<()> { + // empty storage + let mut deps = mock_dependencies(); + let env = mock_env(); + let storage = NymNodeVersionHistory::new(); + + assert!(storage + .id_counter + .may_load(deps.as_mut().storage)? + .is_none()); + + storage.try_insert_new(deps.as_mut().storage, &env, "1.1.1")?; + assert_eq!(storage.id_counter.load(deps.as_mut().storage)?, 0); + + assert_eq!( + storage + .version_history + .load(deps.as_ref().storage, 0)? + .semver, + "1.1.1" + ); + assert_eq!( + storage + .current_version(deps.as_ref().storage)? + .unwrap() + .version_information + .semver, + "1.1.1" + ); + + Ok(()) + } + + #[test] + fn inserting_second_semver() -> anyhow::Result<()> { + let mut test = TestSetup::new(); + let env = test.env(); + let storage = NymNodeVersionHistory::new(); + + // lower version + assert!(storage + .try_insert_new(test.storage_mut(), &env, "1.1.9") + .is_err()); + assert!(storage + .try_insert_new(test.storage_mut(), &env, "1.0.1") + .is_err()); + + // malformed + assert!(storage + .try_insert_new(test.storage_mut(), &env, "1.0") + .is_err()); + assert!(storage + .try_insert_new(test.storage_mut(), &env, "1.0bad") + .is_err()); + assert!(storage + .try_insert_new(test.storage_mut(), &env, "foomp") + .is_err()); + + // patch + let mut test = TestSetup::new(); + storage.try_insert_new(test.storage_mut(), &env, "1.1.11")?; + let current = storage + .current_version(test.storage_mut())? + .unwrap() + .version_information; + assert_eq!(current.semver, "1.1.11"); + assert_eq!(current.difference_since_genesis.major, 0); + assert_eq!(current.difference_since_genesis.minor, 0); + assert_eq!(current.difference_since_genesis.patch, 1); + assert_eq!(current.difference_since_genesis.prerelease, 0); + + // minor + let mut test = TestSetup::new(); + storage.try_insert_new(test.storage_mut(), &env, "1.2.0")?; + let current = storage + .current_version(test.storage_mut())? + .unwrap() + .version_information; + assert_eq!(current.semver, "1.2.0"); + assert_eq!(current.difference_since_genesis.major, 0); + assert_eq!(current.difference_since_genesis.minor, 1); + assert_eq!(current.difference_since_genesis.patch, 0); + assert_eq!(current.difference_since_genesis.prerelease, 0); + + // minor alt. + let mut test = TestSetup::new(); + storage.try_insert_new(test.storage_mut(), &env, "1.2.3")?; + let current = storage + .current_version(test.storage_mut())? + .unwrap() + .version_information; + assert_eq!(current.semver, "1.2.3"); + assert_eq!(current.difference_since_genesis.major, 0); + assert_eq!(current.difference_since_genesis.minor, 1); + assert_eq!(current.difference_since_genesis.patch, 0); + assert_eq!(current.difference_since_genesis.prerelease, 0); + + Ok(()) + } + + #[test] + fn inserting_subsequent_semver() -> anyhow::Result<()> { + let mut test = TestSetup::new(); + let env = test.env(); + let storage = NymNodeVersionHistory::new(); + + storage.try_insert_new(test.storage_mut(), &env, "1.2.0")?; + storage.try_insert_new(test.storage_mut(), &env, "1.2.1")?; + storage.try_insert_new(test.storage_mut(), &env, "1.2.3")?; + let current = storage + .current_version(test.storage_mut())? + .unwrap() + .version_information; + assert_eq!(current.semver, "1.2.3"); + assert_eq!(current.difference_since_genesis.major, 0); + assert_eq!(current.difference_since_genesis.minor, 1); + assert_eq!(current.difference_since_genesis.patch, 3); + assert_eq!(current.difference_since_genesis.prerelease, 0); + + storage.try_insert_new(test.storage_mut(), &env, "1.3.0")?; + let current = storage + .current_version(test.storage_mut())? + .unwrap() + .version_information; + assert_eq!(current.semver, "1.3.0"); + assert_eq!(current.difference_since_genesis.major, 0); + assert_eq!(current.difference_since_genesis.minor, 2); + assert_eq!(current.difference_since_genesis.patch, 3); + assert_eq!(current.difference_since_genesis.prerelease, 0); + Ok(()) + } + } +} diff --git a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs index d3c3a211f1..f48f7b401a 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs @@ -238,4 +238,446 @@ pub mod tests { // let res = try_update_contract_settings(deps.as_mut(), info, new_params); // assert_eq!(Err(MixnetContractError::ZeroActiveSet), res); } + + #[cfg(test)] + mod updating_current_nym_node_semver { + use super::*; + use crate::mixnet_contract_settings::queries::query_current_nym_node_version; + use crate::support::tests::test_helpers::TestSetup; + + #[test] + fn is_restricted_to_the_admin() -> anyhow::Result<()> { + let mut test = TestSetup::new(); + + let not_admin = mock_info("not-admin", &[]); + let admin = mock_info(test.admin().as_ref(), &[]); + + let env = test.env(); + let res = try_update_current_nym_node_semver( + test.deps_mut(), + env, + not_admin, + "1.2.1".to_string(), + ); + assert!(res.is_err()); + + let env = test.env(); + let res = try_update_current_nym_node_semver( + test.deps_mut(), + env, + admin, + "1.2.1".to_string(), + ); + assert!(res.is_ok()); + Ok(()) + } + + #[test] + fn updates_current_semver_value() -> anyhow::Result<()> { + let mut test = TestSetup::new(); + + let res = query_current_nym_node_version(test.deps())?; + + let initial = res.version.unwrap().version_information.semver; + // sanity check to make sure our contract init hasn't changed + assert_eq!(initial, "1.1.10"); + + let update = "1.2.0".to_string(); + + let env = test.env(); + let sender = test.admin_sender(); + try_update_current_nym_node_semver(test.deps_mut(), env, sender, update.clone())?; + + let updated = query_current_nym_node_version(test.deps())?; + let version = updated.version.unwrap().version_information.semver; + assert_eq!(version, update); + + Ok(()) + } + + #[cfg(test)] + mod semver_chain_updates { + use super::*; + use crate::mixnet_contract_settings::queries::query_nym_node_version_history_paged; + use mixnet_contract_common::{ + HistoricalNymNodeVersion, HistoricalNymNodeVersionEntry, TotalVersionDifference, + }; + + fn test_setup_with_initial_checks() -> anyhow::Result { + let test = TestSetup::new(); + + let res = query_current_nym_node_version(test.deps())?; + let initial = res.version.unwrap().version_information.semver; + + // sanity check to make sure our contract init hasn't changed + assert_eq!(initial, "1.1.10"); + + let history = query_nym_node_version_history_paged(test.deps(), None, None)?; + assert_eq!(history.history.len(), 1); + + Ok(test) + } + + #[test] + fn single_patch() -> anyhow::Result<()> { + let mut test = test_setup_with_initial_checks()?; + let initial = query_current_nym_node_version(test.deps())? + .version + .unwrap(); + + let env = test.env(); + let sender = test.admin_sender(); + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender, + "1.1.11".to_string(), + )?; + + let history = + query_nym_node_version_history_paged(test.deps(), None, None)?.history; + assert_eq!(history.len(), 2); + assert_eq!(history[0], initial); + assert_eq!( + history[1], + HistoricalNymNodeVersionEntry { + id: 1, + version_information: HistoricalNymNodeVersion { + semver: "1.1.11".to_string(), + introduced_at_height: env.block.height, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 0, + patch: 1, + prerelease: 0, + }, + }, + } + ); + + Ok(()) + } + + #[test] + fn single_minor() -> anyhow::Result<()> { + let mut test = test_setup_with_initial_checks()?; + let initial = query_current_nym_node_version(test.deps())? + .version + .unwrap(); + + let env = test.env(); + let sender = test.admin_sender(); + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender, + "1.2.0".to_string(), + )?; + + let history = + query_nym_node_version_history_paged(test.deps(), None, None)?.history; + assert_eq!(history.len(), 2); + assert_eq!(history[0], initial); + assert_eq!( + history[1], + HistoricalNymNodeVersionEntry { + id: 1, + version_information: HistoricalNymNodeVersion { + semver: "1.2.0".to_string(), + introduced_at_height: env.block.height, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 1, + patch: 0, + prerelease: 0, + }, + }, + } + ); + + Ok(()) + } + + #[test] + fn multiple_patches() -> anyhow::Result<()> { + let mut test = test_setup_with_initial_checks()?; + let initial = query_current_nym_node_version(test.deps())? + .version + .unwrap(); + + let mut env = test.env(); + let sender = test.admin_sender(); + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender.clone(), + "1.1.11".to_string(), + )?; + env.block.height += 1; + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender.clone(), + "1.1.12".to_string(), + )?; + env.block.height += 1; + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender, + "1.1.13".to_string(), + )?; + + let history = + query_nym_node_version_history_paged(test.deps(), None, None)?.history; + assert_eq!(history.len(), 4); + assert_eq!(history[0], initial); + assert_eq!( + history[1], + HistoricalNymNodeVersionEntry { + id: 1, + version_information: HistoricalNymNodeVersion { + semver: "1.1.11".to_string(), + introduced_at_height: env.block.height - 2, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 0, + patch: 1, + prerelease: 0, + }, + }, + } + ); + assert_eq!( + history[2], + HistoricalNymNodeVersionEntry { + id: 2, + version_information: HistoricalNymNodeVersion { + semver: "1.1.12".to_string(), + introduced_at_height: env.block.height - 1, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 0, + patch: 2, + prerelease: 0, + }, + }, + } + ); + assert_eq!( + history[3], + HistoricalNymNodeVersionEntry { + id: 3, + version_information: HistoricalNymNodeVersion { + semver: "1.1.13".to_string(), + introduced_at_height: env.block.height, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 0, + patch: 3, + prerelease: 0, + }, + }, + } + ); + + Ok(()) + } + + #[test] + fn multiple_minors() -> anyhow::Result<()> { + let mut test = test_setup_with_initial_checks()?; + let initial = query_current_nym_node_version(test.deps())? + .version + .unwrap(); + + let mut env = test.env(); + let sender = test.admin_sender(); + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender.clone(), + "1.2.0".to_string(), + )?; + env.block.height += 1; + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender.clone(), + "1.3.0".to_string(), + )?; + env.block.height += 1; + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender, + "1.4.0".to_string(), + )?; + + let history = + query_nym_node_version_history_paged(test.deps(), None, None)?.history; + assert_eq!(history.len(), 4); + assert_eq!(history[0], initial); + assert_eq!( + history[1], + HistoricalNymNodeVersionEntry { + id: 1, + version_information: HistoricalNymNodeVersion { + semver: "1.2.0".to_string(), + introduced_at_height: env.block.height - 2, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 1, + patch: 0, + prerelease: 0, + }, + }, + } + ); + assert_eq!( + history[2], + HistoricalNymNodeVersionEntry { + id: 2, + version_information: HistoricalNymNodeVersion { + semver: "1.3.0".to_string(), + introduced_at_height: env.block.height - 1, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 2, + patch: 0, + prerelease: 0, + }, + }, + } + ); + assert_eq!( + history[3], + HistoricalNymNodeVersionEntry { + id: 3, + version_information: HistoricalNymNodeVersion { + semver: "1.4.0".to_string(), + introduced_at_height: env.block.height, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 3, + patch: 0, + prerelease: 0, + }, + }, + } + ); + + Ok(()) + } + + #[test] + fn mixed_multiple_updates() -> anyhow::Result<()> { + let mut test = test_setup_with_initial_checks()?; + let initial = query_current_nym_node_version(test.deps())? + .version + .unwrap(); + + let mut env = test.env(); + let sender = test.admin_sender(); + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender.clone(), + "1.2.0".to_string(), + )?; + env.block.height += 1; + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender.clone(), + "1.2.1".to_string(), + )?; + env.block.height += 1; + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender.clone(), + "1.2.3".to_string(), + )?; + env.block.height += 1; + try_update_current_nym_node_semver( + test.deps_mut(), + env.clone(), + sender, + "1.3.0".to_string(), + )?; + + let history = + query_nym_node_version_history_paged(test.deps(), None, None)?.history; + assert_eq!(history.len(), 5); + assert_eq!(history[0], initial); + assert_eq!( + history[1], + HistoricalNymNodeVersionEntry { + id: 1, + version_information: HistoricalNymNodeVersion { + semver: "1.2.0".to_string(), + introduced_at_height: env.block.height - 3, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 1, + patch: 0, + prerelease: 0, + }, + }, + } + ); + assert_eq!( + history[2], + HistoricalNymNodeVersionEntry { + id: 2, + version_information: HistoricalNymNodeVersion { + semver: "1.2.1".to_string(), + introduced_at_height: env.block.height - 2, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 1, + patch: 1, + prerelease: 0, + }, + }, + } + ); + assert_eq!( + history[3], + HistoricalNymNodeVersionEntry { + id: 3, + version_information: HistoricalNymNodeVersion { + semver: "1.2.3".to_string(), + introduced_at_height: env.block.height - 1, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 1, + patch: 3, + prerelease: 0, + }, + }, + } + ); + assert_eq!( + history[4], + HistoricalNymNodeVersionEntry { + id: 4, + version_information: HistoricalNymNodeVersion { + semver: "1.3.0".to_string(), + introduced_at_height: env.block.height, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 2, + patch: 3, + prerelease: 0, + }, + }, + } + ); + + Ok(()) + } + } + } } diff --git a/contracts/mixnet/src/queued_migrations.rs b/contracts/mixnet/src/queued_migrations.rs index 476d484d80..ebcce35112 100644 --- a/contracts/mixnet/src/queued_migrations.rs +++ b/contracts/mixnet/src/queued_migrations.rs @@ -1,86 +1,153 @@ // Copyright 2022-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -mod config_score_params { - use crate::constants::CONTRACT_STATE_KEY; - use crate::mixnet_contract_settings::storage as mixnet_params_storage; +mod node_version_history { use crate::mixnet_contract_settings::storage::NymNodeVersionHistory; - use cosmwasm_std::{Addr, Coin, DepsMut, Env}; - use cw_storage_plus::Item; + use cosmwasm_std::DepsMut; use mixnet_contract_common::error::MixnetContractError; - use mixnet_contract_common::{ - ConfigScoreParams, ContractState, ContractStateParams, DelegationsParams, MigrateMsg, - OperatingCostRange, OperatorsParams, ProfitMarginRange, - }; - use serde::{Deserialize, Serialize}; - use std::str::FromStr; + use mixnet_contract_common::{HistoricalNymNodeVersion, TotalVersionDifference}; - pub(crate) fn add_config_score_params( + pub(crate) fn restore_node_version_history( deps: DepsMut<'_>, - env: Env, - msg: &MigrateMsg, ) -> Result<(), MixnetContractError> { - if semver::Version::from_str(&msg.current_nym_node_semver).is_err() { - return Err(MixnetContractError::InvalidNymNodeSemver { - provided: msg.current_nym_node_semver.to_string(), + // sanity check: + let storage = NymNodeVersionHistory::new(); + let Some(current) = storage.current_version(deps.storage)? else { + return Err(MixnetContractError::FailedMigration { + comment: "no node version history set".to_string(), + }); + }; + if current.version_information.semver != "1.2.1" + || current.version_information.introduced_at_height != 15902170 + { + return Err(MixnetContractError::FailedMigration { + comment: format!("unexpected current node version history. got: {current:?}"), }); } - - #[derive(Serialize, Deserialize)] - pub struct OldContractState { - pub owner: Option, - pub rewarding_validator_address: Addr, - pub vesting_contract_address: Addr, - pub rewarding_denom: String, - pub params: OldContractStateParams, - } - - #[derive(Serialize, Deserialize)] - pub struct OldContractStateParams { - pub minimum_delegation: Option, - pub minimum_pledge: Coin, - #[serde(default)] - pub profit_margin: ProfitMarginRange, - #[serde(default)] - pub interval_operating_cost: OperatingCostRange, - } - - const OLD_CONTRACT_STATE: Item<'_, OldContractState> = Item::new(CONTRACT_STATE_KEY); - let old_state = OLD_CONTRACT_STATE.load(deps.storage)?; - - #[allow(deprecated)] - let new_state = ContractState { - owner: old_state.owner, - rewarding_validator_address: old_state.rewarding_validator_address, - vesting_contract_address: old_state.vesting_contract_address, - rewarding_denom: old_state.rewarding_denom, - params: ContractStateParams { - delegations_params: DelegationsParams { - minimum_delegation: old_state.params.minimum_delegation, - }, - operators_params: OperatorsParams { - minimum_pledge: old_state.params.minimum_pledge, - profit_margin: old_state.params.profit_margin, - interval_operating_cost: old_state.params.interval_operating_cost, - }, - config_score_params: ConfigScoreParams { - version_weights: msg.version_score_weights, - version_score_formula_params: msg.version_score_params, - }, - }, + let lost = HistoricalNymNodeVersion { + semver: "1.1.12".to_string(), + introduced_at_height: 15779133, + difference_since_genesis: TotalVersionDifference::default(), }; - mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?; + #[allow(clippy::unwrap_used)] + // SAFETY: this information was already stored in the contract, so it must be a valid semver + let difference_since_genesis = lost.cumulative_difference_since_genesis( + ¤t.version_information.semver.parse().unwrap(), + ); + let updated_current = HistoricalNymNodeVersion { + semver: current.version_information.semver, + introduced_at_height: current.version_information.introduced_at_height, + difference_since_genesis, + }; - // initialise the version chain - NymNodeVersionHistory::new().try_insert_new( - deps.storage, - &env, - &msg.current_nym_node_semver, - )?; + // restore overwritten entry for 1.1.12 + storage.version_history.save(deps.storage, 0, &lost)?; + + // re-insert 1.2.1 as the current + storage + .version_history + .save(deps.storage, 1, &updated_current)?; + storage.id_counter.save(deps.storage, &1)?; Ok(()) } } -pub(crate) use config_score_params::add_config_score_params; +pub(crate) use node_version_history::restore_node_version_history; + +#[cfg(test)] +mod tests { + use super::*; + use crate::mixnet_contract_settings::queries::query_nym_node_version_history_paged; + use crate::mixnet_contract_settings::storage::NymNodeVersionHistory; + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + use mixnet_contract_common::{ + HistoricalNymNodeVersion, HistoricalNymNodeVersionEntry, TotalVersionDifference, + }; + + #[test] + fn fixing_history_storage() -> anyhow::Result<()> { + // current state on mainnet: + let mut deps = mock_dependencies(); + let storage = NymNodeVersionHistory::new(); + + storage.id_counter.save(deps.as_mut().storage, &0)?; + storage.version_history.save( + deps.as_mut().storage, + 0, + &HistoricalNymNodeVersion { + semver: "1.2.1".to_string(), + introduced_at_height: 15902170, + difference_since_genesis: Default::default(), + }, + )?; + + // run migration + restore_node_version_history(deps.as_mut())?; + + let current = storage.current_version(deps.as_ref().storage)?.unwrap(); + assert_eq!(current.version_information.semver, "1.2.1"); + assert_eq!(current.version_information.introduced_at_height, 15902170); + assert_eq!( + current.version_information.difference_since_genesis, + TotalVersionDifference { + major: 0, + minor: 1, + patch: 0, + prerelease: 0, + } + ); + + let history = query_nym_node_version_history_paged(deps.as_ref(), None, None)?.history; + assert_eq!(history.len(), 2); + assert_eq!( + history, + vec![ + HistoricalNymNodeVersionEntry { + id: 0, + version_information: HistoricalNymNodeVersion { + semver: "1.1.12".to_string(), + introduced_at_height: 15779133, + difference_since_genesis: Default::default(), + }, + }, + HistoricalNymNodeVersionEntry { + id: 1, + version_information: HistoricalNymNodeVersion { + semver: "1.2.1".to_string(), + introduced_at_height: 15902170, + difference_since_genesis: TotalVersionDifference { + major: 0, + minor: 1, + patch: 0, + prerelease: 0, + }, + }, + } + ] + ); + + let counter = storage.id_counter.load(deps.as_ref().storage)?; + assert_eq!(counter, 1); + + // make sure adding another version doesn't mess anything up + storage.try_insert_new(deps.as_mut().storage, &mock_env(), "1.3.0")?; + + let current = storage.current_version(deps.as_ref().storage)?.unwrap(); + assert_eq!(current.version_information.semver, "1.3.0"); + assert_eq!( + current.version_information.difference_since_genesis, + TotalVersionDifference { + major: 0, + minor: 2, + patch: 0, + prerelease: 0, + } + ); + let counter = storage.id_counter.load(deps.as_ref().storage)?; + assert_eq!(counter, 2); + + Ok(()) + } +} diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index cd40ef7ca4..4c0f2d85a3 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -18,7 +18,7 @@ pub mod test_helpers { }; use crate::interval::{pending_events, storage as interval_storage}; use crate::mixnet_contract_settings::storage::{ - self as mixnet_params_storage, minimum_node_pledge, + self as mixnet_params_storage, minimum_node_pledge, ADMIN, }; use crate::mixnet_contract_settings::storage::{rewarding_denom, rewarding_validator_address}; use crate::mixnodes::helpers::get_mixnode_details_by_id; @@ -318,6 +318,10 @@ pub mod test_helpers { compare_decimals(mix_info.delegates, subtotal, Some(epsilon)) } + pub fn admin(&self) -> Addr { + ADMIN.get(self.deps()).unwrap().unwrap() + } + pub fn random_address(&mut self) -> String { format!("n1foomp{}", self.rng.next_u64()) } @@ -330,6 +334,14 @@ pub mod test_helpers { self.deps.as_mut() } + pub fn storage(&self) -> &dyn Storage { + self.deps().storage + } + + pub fn storage_mut(&mut self) -> &mut dyn Storage { + self.deps_mut().storage + } + pub fn env(&self) -> Env { self.env.clone() } @@ -470,6 +482,10 @@ pub mod test_helpers { .unwrap() } + pub fn admin_sender(&self) -> MessageInfo { + mock_info(self.admin().as_ref(), &[]) + } + pub fn owner(&self) -> MessageInfo { self.owner.clone() } diff --git a/documentation/.gitignore b/documentation/.gitignore index 95b579c427..4329a0d6c0 100644 --- a/documentation/.gitignore +++ b/documentation/.gitignore @@ -1 +1,2 @@ todo.md +scripts/generate-api diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-api-build-info-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-api-build-info-help.md deleted file mode 100644 index db52a92b4e..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-api-build-info-help.md +++ /dev/null @@ -1,9 +0,0 @@ -```sh -Show build information of this binary - -Usage: nym-api build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-api-build-info.md b/documentation/autodoc/autodoc-generated-markdown/nym-api-build-info.md deleted file mode 100644 index c04a06ee09..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-api-build-info.md +++ /dev/null @@ -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 - -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-api-commands.md b/documentation/autodoc/autodoc-generated-markdown/nym-api-commands.md deleted file mode 100644 index 185d88277b..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-api-commands.md +++ /dev/null @@ -1,95 +0,0 @@ -# `nym-api` Binary Commands (Autogenerated) - -These docs are autogenerated by the [`autodocs`](https://github.com/nymtech/nym/tree/max/new-docs-framework/documentation/autodoc) script. -```sh -2024-10-29T09:51:44.008469Z  INFO nym-api/src/main.rs:41: Starting nym api... -Usage: nym-api [OPTIONS] - -Commands: - init Initialise a Nym Api instance with persistent config.toml file - run Run the Nym Api with provided configuration optionally overriding set parameters - build-info Show build information of this binary - help Print this message or the help of the given subcommand(s) - -Options: - -c, --config-env-file - Path pointing to an env file that configures the Nym API [env: NYMAPI_CONFIG_ENV_FILE_ARG=] - --no-banner - A no-op flag included for consistency with other binaries (and compatibility with nymvisor, oops) [env: NYMAPI_NO_BANNER_ARG=] - -h, --help - Print help - -V, --version - Print version -``` - -### `init` -```sh -2024-10-29T09:51:44.013292Z  INFO nym-api/src/main.rs:41: Starting nym api... -Initialise a Nym Api instance with persistent config.toml file - -Usage: nym-api init [OPTIONS] - -Options: - --id - Id of the nym-api we want to initialise. if unspecified, a default value will be used. default: "default" [env: NYMAPI_ID_ARG=] [default: default] - -m, --enable-monitor - Specifies whether network monitoring is enabled on this API default: false [env: NYMAPI_ENABLE_MONITOR_ARG=] - -r, --enable-rewarding - Specifies whether network rewarding is enabled on this API default: false [env: NYMAPI_ENABLE_REWARDING_ARG=] - --nyxd-validator - Endpoint to nyxd instance used for contract information. default: http://localhost:26657 [env: NYMAPI_NYXD_VALIDATOR_ARG=] - --mnemonic - Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions default: None [env: NYMAPI_MNEMONIC_ARG=] - --enable-zk-nym - Flag to indicate whether credential signer authority is enabled on this API default: false [env: NYMAPI_ENABLE_ZK_NYM_ARG=] - --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 [env: NYMAPI_ANNOUNCE_ADDRESS_NYM_ARG=] - --monitor-credentials-mode - Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement [env: NYMAPI_MONITOR_CREDENTIALS_MODE_ARG=] - --bind-address - Socket address this api will use for binding its http API. default: `127.0.0.1:8080` in `debug` builds and `0.0.0.0:8080` in `release` - -h, --help - Print help -``` - -### `run` -```sh -2024-10-29T09:51:44.032688Z  INFO nym-api/src/main.rs:41: Starting nym api... -Run the Nym Api with provided configuration optionally overriding set parameters - -Usage: nym-api run [OPTIONS] - -Options: - --id - Id of the nym-api we want to run.if unspecified, a default value will be used. default: "default" [env: NYMAPI_ID_ARG=] [default: default] - -m, --enable-monitor - Specifies whether network monitoring is enabled on this API default: None - config value will be used instead [env: NYMAPI_ENABLE_MONITOR_ARG=] [possible values: true, false] - -r, --enable-rewarding - Specifies whether network rewarding is enabled on this API default: None - config value will be used instead [env: NYMAPI_ENABLE_REWARDING_ARG=] [possible values: true, false] - --nyxd-validator - Endpoint to nyxd instance used for contract information. default: None - config value will be used instead [env: NYMAPI_NYXD_VALIDATOR_ARG=] - --mnemonic - Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions default: None - config value will be used instead [env: NYMAPI_MNEMONIC_ARG=] - --enable-zk-nym - Flag to indicate whether coconut signer authority is enabled on this API default: None - config value will be used instead [env: NYMAPI_ENABLE_ZK_NYM_ARG=] [possible values: true, false] - --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 - config value will be used instead [env: NYMAPI_ANNOUNCE_ADDRESS_ARG=] - --monitor-credentials-mode - Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement default: None - config value will be used instead [env: NYMAPI_MONITOR_CREDENTIALS_MODE_ARG=] [possible values: true, false] - --bind-address - Socket address this api will use for binding its http API. default: `127.0.0.1:8080` in `debug` builds and `0.0.0.0:8080` in `release` - -h, --help - Print help -``` - -### `build-info` -```sh -2024-10-29T09:51:44.037915Z  INFO nym-api/src/main.rs:41: Starting nym api... -Show build information of this binary - -Usage: nym-api build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-api-init-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-api-init-help.md deleted file mode 100644 index bd4c714a28..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-api-init-help.md +++ /dev/null @@ -1,25 +0,0 @@ -```sh -Initialise a Nym Api instance with persistent config.toml file - -Usage: nym-api init [OPTIONS] - -Options: - --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 - Endpoint to nyxd instance used for contract information. default: http://localhost:26657 - --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 - 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 -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-build-info-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-build-info-help.md deleted file mode 100644 index f65dc4e7c4..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-build-info-help.md +++ /dev/null @@ -1,9 +0,0 @@ -```sh -Show build information of this binary - -Usage: nym-client build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-build-info.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-build-info.md deleted file mode 100644 index a0c7d2c612..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-build-info.md +++ /dev/null @@ -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 - -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-completions-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-completions-help.md deleted file mode 100644 index f09b17f785..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-completions-help.md +++ /dev/null @@ -1,11 +0,0 @@ -```sh -Generate shell completions - -Usage: nym-client completions - -Arguments: - [possible values: bash, elvish, fish, power-shell, zsh] - -Options: - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-generate-fig-spec-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-generate-fig-spec-help.md deleted file mode 100644 index ecce720ea9..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-generate-fig-spec-help.md +++ /dev/null @@ -1,8 +0,0 @@ -```sh -Generate Fig specification - -Usage: nym-client generate-fig-spec - -Options: - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-generate-fig-spec.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-generate-fig-spec.md deleted file mode 100644 index 154c25a968..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-generate-fig-spec.md +++ /dev/null @@ -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; -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-import-credential-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-import-credential-help.md deleted file mode 100644 index 2e1b652e39..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-import-credential-help.md +++ /dev/null @@ -1,15 +0,0 @@ -```sh -Import a pre-generated credential - -Usage: nym-client import-credential --id <--credential-data |--credential-path > - -Options: - --id - Id of client that is going to import the credential - --credential-data - Explicitly provide the encoded credential data (as base58) - --credential-path - Specifies the path to file containing binary credential data - -h, --help - Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-init-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-init-help.md deleted file mode 100644 index 3d58fe52c2..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-init-help.md +++ /dev/null @@ -1,27 +0,0 @@ -```sh -Initialise a Nym client. Do this first! - -Usage: nym-client init [OPTIONS] --id - -Options: - --id - Id of client we want to create config for - --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 - Comma separated list of rest endpoints of the API validators - --disable-socket - Whether to not start the websocket [possible values: true, false] - -p, --port - Port for the socket (if applicable) to listen on in all subsequent runs - --host - Ip for the socket (if applicable) to listen for requests - -o, --output - [default: text] [possible values: text, json] - -h, --help - Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-list-gateways-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-list-gateways-help.md deleted file mode 100644 index 2cddc84edc..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-list-gateways-help.md +++ /dev/null @@ -1,10 +0,0 @@ -```sh -List all registered with gateways - -Usage: nym-client list-gateways [OPTIONS] --id - -Options: - --id Id of client we want to list gateways for - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-run-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-run-help.md deleted file mode 100644 index deb33eab62..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-run-help.md +++ /dev/null @@ -1,21 +0,0 @@ -```sh -Run the Nym client with provided configuration client optionally overriding set parameters - -Usage: nym-client run [OPTIONS] --id - -Options: - --id - Id of client we want to create config for - --gateway - Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened - --nym-apis - Comma separated list of rest endpoints of the API validators - --disable-socket - Whether to not start the websocket [possible values: true, false] - -p, --port - Port for the socket to listen on - --host - Ip for the socket (if applicable) to listen for requests - -h, --help - Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-client-switch-gateway-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-client-switch-gateway-help.md deleted file mode 100644 index 53277ffdd2..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-client-switch-gateway-help.md +++ /dev/null @@ -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 --gateway-id - -Options: - --id Id of client we want to list gateways for - --gateway-id Id of the gateway we want to switch to - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-bonding-information-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-bonding-information-help.md deleted file mode 100644 index 8a54a3c853..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-bonding-information-help.md +++ /dev/null @@ -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 of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file Path to a configuration file of this node [env: NYMNODE_CONFIG=] - --mode [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway] - -o, --output Specify the output format of the bonding information (`text` or `json`) [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-bonding-information.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-bonding-information.md deleted file mode 100644 index edc2294fb2..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-bonding-information.md +++ /dev/null @@ -1,3 +0,0 @@ -```sh -2024-10-22T13:00:32.542213Z ERROR nym-node/src/config/upgrade_helpers.rs:16: Failed to finish upgrade - failed to load config file using path '/home/m/.nym/nym-nodes/default-nym-node/config/config.toml'. detailed message: No such file or directory (os error 2) -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-build-info-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-build-info-help.md deleted file mode 100644 index 0f369b8537..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-build-info-help.md +++ /dev/null @@ -1,9 +0,0 @@ -```sh -Show build information of this binary - -Usage: nym-node build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-build-info.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-build-info.md deleted file mode 100644 index 8113e9c0a8..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-build-info.md +++ /dev/null @@ -1,13 +0,0 @@ -```sh - -Binary Name: nym-node -Build Timestamp: 2024-10-09T13:56:14.428750844Z -Build Version: 1.1.6 -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 - -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-commands.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-commands.md deleted file mode 100644 index f305d5c6eb..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-commands.md +++ /dev/null @@ -1,237 +0,0 @@ -# `nym-node` Binary Commands (Autogenerated) - -These docs are autogenerated by the [`autodocs`](https://github.com/nymtech/nym/tree/max/new-docs-framework/documentation/autodoc) script. -```sh -Usage: nym-node [OPTIONS] - -Commands: - build-info Show build information of this binary - bonding-information Show bonding information of this node depending on its currently selected mode - node-details Show details of this node - migrate Attempt to migrate an existing mixnode or gateway into a nym-node - run Start this nym-node - sign Use identity key of this node to sign provided message - help Print this message or the help of the given subcommand(s) - -Options: - -c, --config-env-file - Path pointing to an env file that configures the nym-node and overrides any preconfigured values [env: NYMNODE_CONFIG_ENV_FILE_ARG=] - --no-banner - Flag used for disabling the printed banner in tty [env: NYMNODE_NO_BANNER=] - -h, --help - Print help - -V, --version - Print version -``` - -### `build-info` -```sh -Show build information of this binary - -Usage: nym-node build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` - -### `bonding-information` -```sh -Show bonding information of this node depending on its currently selected mode - -Usage: nym-node bonding-information [OPTIONS] - -Options: - --id Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file Path to a configuration file of this node [env: NYMNODE_CONFIG=] - --mode [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway] - -o, --output Specify the output format of the bonding information (`text` or `json`) [default: text] [possible values: text, json] - -h, --help Print help -``` - -### `node-details` -```sh -Show details of this node - -Usage: nym-node node-details [OPTIONS] - -Options: - --id Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file Path to a configuration file of this node [env: NYMNODE_CONFIG=] - -o, --output Specify the output format of the node details (`text` or `json`) [default: text] [possible values: text, json] - -h, --help Print help -``` - -### `migrate` -```sh -Attempt to migrate an existing mixnode or gateway into a nym-node - -Usage: nym-node migrate [OPTIONS] <--id |--config-file > - -Arguments: - Type of node (mixnode or gateway) to migrate into a nym-node [possible values: mixnode, gateway] - -Options: - --id - Id of the node that's going to get migrated - --config-file - Path to a configuration file of the node that's going to get migrated - --preserve-id - Specify whether to preserve id of the imported node - --public-ips - Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. In nearly all circumstances, it's going to be identical to the address you're going to use for bonding [env: NYMNODE_PUBLIC_IPS=] - --hostname - Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=] - --location - Optional **physical** location of this node's server. Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. 'PL'), three-letter alpha3 (e.g. 'POL') or three-digit numeric-3 (e.g. '616') can be provided [env: NYMNODE_LOCATION=] - --http-bind-address - Socket address this node will use for binding its http API. default: `0.0.0.0:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=] - --landing-page-assets-path - Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=] - --http-access-token - An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=] - --expose-system-info - Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false] - --expose-system-hardware - Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false] - --expose-crypto-hardware - Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false] - --mixnet-bind-address - Address this node will bind to for listening for mixnet packets default: `0.0.0.0:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=] - --mixnet-announce-port - If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_MIXNET_ANNOUNCE_PORT=] - --nym-api-urls - Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=] - --nyxd-urls - Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=] - --wireguard-enabled - Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false] - --wireguard-bind-address - Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=] - --wireguard-private-ip - Private IP address of the wireguard gateway. default: `10.1.0.1` [env: NYMNODE_WG_IP=] - --wireguard-announced-port - Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] - --wireguard-private-network-prefix - The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=] - --verloc-bind-address - Socket address this node will use for binding its verloc API. default: `0.0.0.0:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=] - --verloc-announce-port - If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_VERLOC_ANNOUNCE_PORT=] - --entry-bind-address - Socket address this node will use for binding its client websocket API. default: `0.0.0.0:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=] - --announce-ws-port - Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=] - --announce-wss-port - If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=] - --enforce-zk-nyms - Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible values: true, false] - --mnemonic - Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=] - --upstream-exit-policy-url - Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=] - --open-proxy - Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, false] - -h, --help - Print help -``` - -### `run` -```sh -Start this nym-node - -Usage: nym-node run [OPTIONS] - -Options: - --id - Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file - Path to a configuration file of this node [env: NYMNODE_CONFIG=] - --accept-operator-terms-and-conditions - Explicitly specify whether you agree with the terms and conditions of a nym node operator as defined at [env: NYMNODE_ACCEPT_OPERATOR_TERMS=] - --deny-init - Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist [env: NYMNODE_DENY_INIT=] - --init-only - If this is a brand new nym-node, specify whether it should only be initialised without actually running the subprocesses [env: NYMNODE_INIT_ONLY=] - --local - Flag specifying this node will be running in a local setting [env: NYMNODE_LOCAL=] - --mode - Specifies the current mode of this nym-node [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway] - -w, --write-changes - If this node has been initialised before, specify whether to write any new changes to the config file [env: NYMNODE_WRITE_CONFIG_CHANGES=] - --bonding-information-output - Specify output file for bonding information of this nym-node, i.e. its encoded keys. NOTE: the required bonding information is still a subject to change and this argument should be treated only as a preview of future features [env: NYMNODE_BONDING_INFORMATION_OUTPUT=] - -o, --output - Specify the output format of the bonding information (`text` or `json`) [env: NYMNODE_OUTPUT=] [default: text] [possible values: text, json] - --public-ips - Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. In nearly all circumstances, it's going to be identical to the address you're going to use for bonding [env: NYMNODE_PUBLIC_IPS=] - --hostname - Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=] - --location - Optional **physical** location of this node's server. Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. 'PL'), three-letter alpha3 (e.g. 'POL') or three-digit numeric-3 (e.g. '616') can be provided [env: NYMNODE_LOCATION=] - --http-bind-address - Socket address this node will use for binding its http API. default: `0.0.0.0:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=] - --landing-page-assets-path - Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=] - --http-access-token - An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=] - --expose-system-info - Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false] - --expose-system-hardware - Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false] - --expose-crypto-hardware - Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false] - --mixnet-bind-address - Address this node will bind to for listening for mixnet packets default: `0.0.0.0:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=] - --mixnet-announce-port - If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_MIXNET_ANNOUNCE_PORT=] - --nym-api-urls - Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=] - --nyxd-urls - Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=] - --wireguard-enabled - Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false] - --wireguard-bind-address - Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=] - --wireguard-private-ip - Private IP address of the wireguard gateway. default: `10.1.0.1` [env: NYMNODE_WG_IP=] - --wireguard-announced-port - Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] - --wireguard-private-network-prefix - The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=] - --verloc-bind-address - Socket address this node will use for binding its verloc API. default: `0.0.0.0:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=] - --verloc-announce-port - If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_VERLOC_ANNOUNCE_PORT=] - --entry-bind-address - Socket address this node will use for binding its client websocket API. default: `0.0.0.0:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=] - --announce-ws-port - Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=] - --announce-wss-port - If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=] - --enforce-zk-nyms - Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible values: true, false] - --mnemonic - Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=] - --upstream-exit-policy-url - Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=] - --open-proxy - Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, false] - -h, --help - Print help -``` - -### `sign` -```sh -Use identity key of this node to sign provided message - -Usage: nym-node sign [OPTIONS] <--text |--contract-msg > - -Options: - --id Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file Path to a configuration file of this node [env: NYMNODE_CONFIG=] - --text Signs an arbitrary piece of text with your identity key - --contract-msg Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-migrate-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-migrate-help.md deleted file mode 100644 index ee32ab638b..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-migrate-help.md +++ /dev/null @@ -1,68 +0,0 @@ -```sh -Attempt to migrate an existing mixnode or gateway into a nym-node - -Usage: nym-node migrate [OPTIONS] <--id |--config-file > - -Arguments: - Type of node (mixnode or gateway) to migrate into a nym-node [possible values: mixnode, gateway] - -Options: - --id - Id of the node that's going to get migrated - --config-file - Path to a configuration file of the node that's going to get migrated - --preserve-id - Specify whether to preserve id of the imported node - --public-ips - Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. In nearly all circumstances, it's going to be identical to the address you're going to use for bonding [env: NYMNODE_PUBLIC_IPS=] - --hostname - Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=] - --location - Optional **physical** location of this node's server. Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. 'PL'), three-letter alpha3 (e.g. 'POL') or three-digit numeric-3 (e.g. '616') can be provided [env: NYMNODE_LOCATION=] - --http-bind-address - Socket address this node will use for binding its http API. default: `0.0.0.0:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=] - --landing-page-assets-path - Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=] - --http-access-token - An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=] - --expose-system-info - Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false] - --expose-system-hardware - Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false] - --expose-crypto-hardware - Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false] - --mixnet-bind-address - Address this node will bind to for listening for mixnet packets default: `0.0.0.0:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=] - --nym-api-urls - Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=] - --nyxd-urls - Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=] - --wireguard-enabled - Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false] - --wireguard-bind-address - Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=] - --wireguard-private-ip - Private IP address of the wireguard gateway. default: `10.1.0.1` [env: NYMNODE_WG_IP=] - --wireguard-announced-port - Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] - --wireguard-private-network-prefix - The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=] - --verloc-bind-address - Socket address this node will use for binding its verloc API. default: `0.0.0.0:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=] - --entry-bind-address - Socket address this node will use for binding its client websocket API. default: `0.0.0.0:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=] - --announce-ws-port - Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=] - --announce-wss-port - If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=] - --enforce-zk-nyms - Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible values: true, false] - --mnemonic - Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=] - --upstream-exit-policy-url - Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=] - --open-proxy - Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, false] - -h, --help - Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-node-details-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-node-details-help.md deleted file mode 100644 index cad77bc2a8..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-node-details-help.md +++ /dev/null @@ -1,11 +0,0 @@ -```sh -Show details of this node - -Usage: nym-node node-details [OPTIONS] - -Options: - --id Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file Path to a configuration file of this node [env: NYMNODE_CONFIG=] - -o, --output Specify the output format of the node details (`text` or `json`) [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-node-details.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-node-details.md deleted file mode 100644 index c2b0a34c97..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-node-details.md +++ /dev/null @@ -1,3 +0,0 @@ -```sh -2024-10-22T13:00:32.562750Z ERROR nym-node/src/config/upgrade_helpers.rs:16: Failed to finish upgrade - failed to load config file using path '/home/m/.nym/nym-nodes/default-nym-node/config/config.toml'. detailed message: No such file or directory (os error 2) -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-node-sign-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-node-sign-help.md deleted file mode 100644 index 5d26baf2f2..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-node-sign-help.md +++ /dev/null @@ -1,13 +0,0 @@ -```sh -Use identity key of this node to sign provided message - -Usage: nym-node sign [OPTIONS] <--text |--contract-msg > - -Options: - --id Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file Path to a configuration file of this node [env: NYMNODE_CONFIG=] - --text Signs an arbitrary piece of text with your identity key - --contract-msg Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-add-gateway-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-add-gateway-help.md deleted file mode 100644 index ee209bdcb0..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-add-gateway-help.md +++ /dev/null @@ -1,15 +0,0 @@ -```sh -Add new gateway to this client - -Usage: nym-socks5-client add-gateway [OPTIONS] --id - -Options: - --id Id of client we want to add gateway for - --gateway-id Explicitly specify id of the gateway to register with. If unspecified, a random gateway will be chosen instead - --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 - --set-active Specify whether this new gateway should be set as the active one - --nym-apis Comma separated list of rest endpoints of the API validators - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-build-info-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-build-info-help.md deleted file mode 100644 index 000204a113..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-build-info-help.md +++ /dev/null @@ -1,9 +0,0 @@ -```sh -Show build information of this binary - -Usage: nym-socks5-client build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-build-info.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-build-info.md deleted file mode 100644 index dbf3e77072..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-build-info.md +++ /dev/null @@ -1,13 +0,0 @@ -```sh - -Binary Name: nym-socks5-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 - -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-completions-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-completions-help.md deleted file mode 100644 index 8b780c6f17..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-completions-help.md +++ /dev/null @@ -1,11 +0,0 @@ -```sh -Generate shell completions - -Usage: nym-socks5-client completions - -Arguments: - [possible values: bash, elvish, fish, power-shell, zsh] - -Options: - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-generate-fig-spec-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-generate-fig-spec-help.md deleted file mode 100644 index 94340b5898..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-generate-fig-spec-help.md +++ /dev/null @@ -1,8 +0,0 @@ -```sh -Generate Fig specification - -Usage: nym-socks5-client generate-fig-spec - -Options: - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-generate-fig-spec.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-generate-fig-spec.md deleted file mode 100644 index b76ef9d558..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-generate-fig-spec.md +++ /dev/null @@ -1,619 +0,0 @@ -```sh -const completion: Fig.Spec = { - name: "nym-socks5-client", - description: "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address", - 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: "--provider", - description: "Address of the socks5 provider to send messages to", - isRepeatable: true, - args: { - name: "provider", - }, - }, - { - name: "--use-reply-surbs", - description: "Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. While this is going to hide its actual address information, it will make the actual communication slower and consume nearly double the bandwidth as it will require sending reply SURBs", - isRepeatable: true, - args: { - name: "use_reply_surbs", - isOptional: true, - suggestions: [ - "true", - "false", - ], - }, - }, - { - name: ["-p", "--port"], - description: "Port for the socket to listen on in all subsequent runs", - isRepeatable: true, - args: { - name: "port", - isOptional: true, - }, - }, - { - name: "--host", - description: "The custom host on which the socks5 client will be listening 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 (see more with '--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: "--use-anonymous-replies", - description: "Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. While this is going to hide its actual address information, it will make the actual communication slower and consume nearly double the bandwidth as it will require sending reply SURBs", - isRepeatable: true, - args: { - name: "use_anonymous_replies", - isOptional: true, - suggestions: [ - "true", - "false", - ], - }, - }, - { - name: "--provider", - description: "Address of the socks5 provider to send messages to", - isRepeatable: true, - args: { - name: "provider", - isOptional: true, - }, - }, - { - name: ["-p", "--port"], - description: "Port for the socket to listen on", - isRepeatable: true, - args: { - name: "port", - isOptional: true, - }, - }, - { - name: "--host", - description: "The custom host on which the socks5 client will be listening for requests", - isRepeatable: true, - args: { - name: "host", - isOptional: true, - }, - }, - { - name: "--geo-routing", - description: "Set geo-aware mixnode selection when sending mixnet traffic, for experiments only", - hidden: true, - isRepeatable: true, - args: { - name: "geo_routing", - 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: "--medium-toggle", - description: "Enable medium mixnet traffic, for experiments only. This includes things like disabling cover traffic, no per hop delays, etc", - }, - { - name: "--outfox", - }, - { - name: ["-h", "--help"], - description: "Print help (see more with '--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; -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-import-credential-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-import-credential-help.md deleted file mode 100644 index 2f7303c2e3..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-import-credential-help.md +++ /dev/null @@ -1,15 +0,0 @@ -```sh -Import a pre-generated credential - -Usage: nym-socks5-client import-credential --id <--credential-data |--credential-path > - -Options: - --id - Id of client that is going to import the credential - --credential-data - Explicitly provide the encoded credential data (as base58) - --credential-path - Specifies the path to file containing binary credential data - -h, --help - Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-init-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-init-help.md deleted file mode 100644 index b69ef5f77c..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-init-help.md +++ /dev/null @@ -1,44 +0,0 @@ -```sh -Initialise a Nym client. Do this first! - -Usage: nym-socks5-client init [OPTIONS] --id --provider - -Options: - --id - Id of client we want to create config for - - --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 - Comma separated list of rest endpoints of the API validators - - --provider - Address of the socks5 provider to send messages to - - --use-reply-surbs - Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. While this is going to hide its actual address information, it will make the actual communication slower and consume nearly double the bandwidth as it will require sending reply SURBs. - - Note that some service providers might not support this. - - [possible values: true, false] - - -p, --port - Port for the socket to listen on in all subsequent runs - - --host - The custom host on which the socks5 client will be listening for requests - - -o, --output - [default: text] - [possible values: text, json] - - -h, --help - Print help (see a summary with '-h') -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-list-gateways-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-list-gateways-help.md deleted file mode 100644 index 86921bcf67..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-list-gateways-help.md +++ /dev/null @@ -1,10 +0,0 @@ -```sh -List all registered with gateways - -Usage: nym-socks5-client list-gateways [OPTIONS] --id - -Options: - --id Id of client we want to list gateways for - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-run-help.md b/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-run-help.md deleted file mode 100644 index 2ffccb39ce..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nym-socks5-client-run-help.md +++ /dev/null @@ -1,34 +0,0 @@ -```sh -Run the Nym client with provided configuration client optionally overriding set parameters - -Usage: nym-socks5-client run [OPTIONS] --id - -Options: - --id - Id of client we want to create config for - - --gateway - Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened - - --nym-apis - Comma separated list of rest endpoints of the API validators - - --use-anonymous-replies - Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. While this is going to hide its actual address information, it will make the actual communication slower and consume nearly double the bandwidth as it will require sending reply SURBs. - - Note that some service providers might not support this. - - [possible values: true, false] - - --provider - Address of the socks5 provider to send messages to - - -p, --port - Port for the socket to listen on - - --host - The custom host on which the socks5 client will be listening for requests - - -h, --help - Print help (see a summary with '-h') -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nymvisor-add-upgrade-help.md b/documentation/autodoc/autodoc-generated-markdown/nymvisor-add-upgrade-help.md deleted file mode 100644 index b88f5e1052..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nymvisor-add-upgrade-help.md +++ /dev/null @@ -1,19 +0,0 @@ -```sh -Queues up another upgrade for the associated daemon - -Usage: nymvisor add-upgrade [OPTIONS] --upgrade-name - -Arguments: - Path to the daemon's upgrade executable - -Options: - --upgrade-name Name of this upgrade - --force Overwrite existing upgrade binary / upgrade-info.json file - --add-binary Indicate that this command should only add binary to an *existing* scheduled upgrade - --now Force the upgrade to happen immediately - --publish-date Specifies the publish date metadata field of this upgrade. If unset, the current time will be used - --upgrade-time Specifies the time at which the provided upgrade will be performed (RFC3339 formatted). If left unset, the upgrade will be performed in 15min - --upgrade-delay Specifies delay until the provided upgrade is going to get performed. If let unset, the upgrade will be performed in 15min - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nymvisor-build-info-help.md b/documentation/autodoc/autodoc-generated-markdown/nymvisor-build-info-help.md deleted file mode 100644 index 3a5ccb9b5b..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nymvisor-build-info-help.md +++ /dev/null @@ -1,9 +0,0 @@ -```sh -Show build information of this binary - -Usage: nymvisor build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nymvisor-build-info.md b/documentation/autodoc/autodoc-generated-markdown/nymvisor-build-info.md deleted file mode 100644 index f0a0032042..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nymvisor-build-info.md +++ /dev/null @@ -1,13 +0,0 @@ -```sh - -Binary Name: nymvisor -Build Timestamp: 2024-10-09T13:56:14.428750844Z -Build Version: 0.1.5 -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 - -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nymvisor-commands.md b/documentation/autodoc/autodoc-generated-markdown/nymvisor-commands.md deleted file mode 100644 index 9d0ee36f56..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nymvisor-commands.md +++ /dev/null @@ -1,154 +0,0 @@ -# `nymvisor` Binary Commands (Autogenerated) - -These docs are autogenerated by the [`autodocs`](https://github.com/nymtech/nym/tree/max/new-docs-framework/documentation/autodoc) script. -```sh -Usage: nymvisor [OPTIONS] - -Commands: - init Initialise a nymvisor instance with persistent Config.toml file - run Run the associated daemon with the preconfigured settings - build-info Show build information of this binary - daemon-build-info Show build information of the associated daemon - add-upgrade Queues up another upgrade for the associated daemon - config Show configuration options being used by this instance of nymvisor - help Print this message or the help of the given subcommand(s) - -Options: - -c, --config-env-file - Path pointing to an env file that configures the nymvisor and overrides any preconfigured values - -h, --help - Print help - -V, --version - Print version -``` - -### `init` -```sh -Initialise a nymvisor instance with persistent Config.toml file - -Usage: nymvisor init [OPTIONS] - -Arguments: - Path to the daemon's executable - -Options: - --id - ID specifies the human readable ID of this particular nymvisor instance. Can be overridden with $NYMVISOR_ID environmental variable - --upstream-base-upgrade-url - Sets the base url of the upstream source for obtaining upgrade information for the deaemon. It will be used fo constructing the full url, i.e. $NYMVISOR_UPSTREAM_BASE_UPGRADE_URL/$DAEMON_NAME/upgrade-info.json Can be overridden with $NYMVISOR_UPSTREAM_BASE_UPGRADE_URL environmental variable - --upstream-polling-rate - Specifies the rate of polling the upstream url for upgrade information. default: 1h Can be overridden with $NYMVISOR_UPSTREAM_POLLING_RATE - --disable-nymvisor-logs - If enabled, this will disable `nymvisor` logs (but not the underlying process) Can be overridden with $NYMVISOR_DISABLE_LOGS environmental variable - --upgrade-data-directory - Set custom directory for upgrade data - binaries and upgrade plans. If not set, the global nymvisors' data directory will be used instead. Can be overridden with $NYMVISOR_UPGRADE_DATA_DIRECTORY environmental variable - --daemon-home - The location where the `nymvisor/` directory is kept that contains the auxiliary files associated with the underlying daemon, such as any backups or current version information. (e.g. $HOME/.nym/nym-api/my-nym-api, $HOME/.nym/mixnodes/my-mixnode, etc.). Can be overridden with $DAEMON_HOME environmental variable - --daemon-absolute-upstream-upgrade-url - Override url to the upstream source for upgrade plans for this daeamon. The Url has to point to an endpoint containing a valid [`UpgradeInfo`] json. Note: if set this takes precedence over `upstream_base_upgrade_url` Can be overridden with $DAEMON_ABSOLUTE_UPSTREAM_UPGRADE_URL environmental variable - --allow-download-upgrade-binaries - If set to true, this will enable auto-downloading of new binaries using the url provided in the `upgrade-info.json` Can be overridden with $DAEMON_ALLOW_BINARIES_DOWNLOAD environmental variable [possible values: true, false] - --enforce-download-checksum - If enabled nymvisor will require that a checksum is provided in the upgrade plan for the binary to be downloaded. If disabled, nymvisor will not require a checksum to be provided, but still check the checksum if one is provided. Can be overridden with $DAEMON_ENFORCE_DOWNLOAD_CHECKSUM environmental variable [possible values: true, false] - --restart-daemon-after-upgrade - If enabled, nymvisor will restart the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. Otherwise (if disabled), nymvisor will stop running after an upgrade and will require the system administrator to manually restart it. Note restart is only after the upgrade and does not auto-restart the subprocess after an error occurs. Can be overridden with $DAEMON_RESTART_AFTER_UPGRADE environmental variable [possible values: true, false] - --restart-daemon-on-failure - If enabled, nymvisor will restart the subprocess with the same command-line arguments and flags after it has crashed Can be overridden with $DAEMON_RESTART_ON_FAILURE environmental variable - --on-failure-daemon-restart-delay - If `restart_on_failure` is enabled, the following value defines the amount of time `nymvisor` shall wait before restarting the subprocess. Can be overridden with $DAEMON_FAILURE_RESTART_DELAY environmental variable - --max-daemon-startup-failures - Defines the maximum number of startup failures the subprocess can experience in a quick succession before no further restarts will be attempted and `nymvisor` will exit. Can be overridden with $DAEMON_MAX_STARTUP_FAILURES environmental variable - --startup-period-duration - Defines the length of time during which the subprocess is still considered to be in the startup phase when its failures are going to be considered in `max_startup_failures`. Can be overridden with $DAEMON_STARTUP_PERIOD_DURATION environmental variable - --daemon-shutdown-grace-period - Specifies the amount of time `nymvisor` is willing to wait for the subprocess to undergo graceful shutdown after receiving an interrupt (for either an upgrade or shutdown of the `nymvisor` itself) Once the time passes, a kill signal is going to be sent instead. Can be overridden with $DAEMON_SHUTDOWN_GRACE_PERIOD environmental variable - --daemon-backup-data-directory - Set custom backup directory for daemon data. If not set, the daemon's home directory will be used instead. Can be overridden with $DAEMON_BACKUP_DATA_DIRECTORY environmental variable - --unsafe-skip-backup - If enabled, `nymvisor` will perform upgrades directly without performing any backups. default: false Can be overridden with $DAEMON_UNSAFE_SKIP_BACKUP environmental variable - -o, --output - [default: text] [possible values: text, json] - -h, --help - Print help -``` - -### `run` -```sh -Run the associated daemon with the preconfigured settings - -Usage: nymvisor run [DAEMON_ARGS]... - -Arguments: - [DAEMON_ARGS]... - -Options: - -h, --help Print help -``` - -### `build-info` -```sh -Show build information of this binary - -Usage: nymvisor build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` -Example output: -```sh - -Binary Name: nymvisor -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 0.1.8 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable -cargo Profile: release - -``` - -### `daemon-build-info` -```sh -Show build information of the associated daemon - -Usage: nymvisor daemon-build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` - -### `add-upgrade` -```sh -Queues up another upgrade for the associated daemon - -Usage: nymvisor add-upgrade [OPTIONS] --upgrade-name - -Arguments: - Path to the daemon's upgrade executable - -Options: - --upgrade-name Name of this upgrade - --force Overwrite existing upgrade binary / upgrade-info.json file - --add-binary Indicate that this command should only add binary to an *existing* scheduled upgrade - --now Force the upgrade to happen immediately - --publish-date Specifies the publish date metadata field of this upgrade. If unset, the current time will be used - --upgrade-time Specifies the time at which the provided upgrade will be performed (RFC3339 formatted). If left unset, the upgrade will be performed in 15min - --upgrade-delay Specifies delay until the provided upgrade is going to get performed. If let unset, the upgrade will be performed in 15min - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` - -### `config` -```sh -Show configuration options being used by this instance of nymvisor - -Usage: nymvisor config [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nymvisor-config-help.md b/documentation/autodoc/autodoc-generated-markdown/nymvisor-config-help.md deleted file mode 100644 index 696d346802..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nymvisor-config-help.md +++ /dev/null @@ -1,9 +0,0 @@ -```sh -Show configuration options being used by this instance of nymvisor - -Usage: nymvisor config [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nymvisor-daemon-build-info-help.md b/documentation/autodoc/autodoc-generated-markdown/nymvisor-daemon-build-info-help.md deleted file mode 100644 index c89ba02c59..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nymvisor-daemon-build-info-help.md +++ /dev/null @@ -1,9 +0,0 @@ -```sh -Show build information of the associated daemon - -Usage: nymvisor daemon-build-info [OPTIONS] - -Options: - -o, --output [default: text] [possible values: text, json] - -h, --help Print help -``` diff --git a/documentation/autodoc/autodoc-generated-markdown/nymvisor-init-help.md b/documentation/autodoc/autodoc-generated-markdown/nymvisor-init-help.md deleted file mode 100644 index 8e373fc42c..0000000000 --- a/documentation/autodoc/autodoc-generated-markdown/nymvisor-init-help.md +++ /dev/null @@ -1,48 +0,0 @@ -```sh -Initialise a nymvisor instance with persistent Config.toml file - -Usage: nymvisor init [OPTIONS] - -Arguments: - Path to the daemon's executable - -Options: - --id - ID specifies the human readable ID of this particular nymvisor instance. Can be overridden with $NYMVISOR_ID environmental variable - --upstream-base-upgrade-url - Sets the base url of the upstream source for obtaining upgrade information for the deaemon. It will be used fo constructing the full url, i.e. $NYMVISOR_UPSTREAM_BASE_UPGRADE_URL/$DAEMON_NAME/upgrade-info.json Can be overridden with $NYMVISOR_UPSTREAM_BASE_UPGRADE_URL environmental variable - --upstream-polling-rate - Specifies the rate of polling the upstream url for upgrade information. default: 1h Can be overridden with $NYMVISOR_UPSTREAM_POLLING_RATE - --disable-nymvisor-logs - If enabled, this will disable `nymvisor` logs (but not the underlying process) Can be overridden with $NYMVISOR_DISABLE_LOGS environmental variable - --upgrade-data-directory - Set custom directory for upgrade data - binaries and upgrade plans. If not set, the global nymvisors' data directory will be used instead. Can be overridden with $NYMVISOR_UPGRADE_DATA_DIRECTORY environmental variable - --daemon-home - The location where the `nymvisor/` directory is kept that contains the auxiliary files associated with the underlying daemon, such as any backups or current version information. (e.g. $HOME/.nym/nym-api/my-nym-api, $HOME/.nym/mixnodes/my-mixnode, etc.). Can be overridden with $DAEMON_HOME environmental variable - --daemon-absolute-upstream-upgrade-url - Override url to the upstream source for upgrade plans for this daeamon. The Url has to point to an endpoint containing a valid [`UpgradeInfo`] json. Note: if set this takes precedence over `upstream_base_upgrade_url` Can be overridden with $DAEMON_ABSOLUTE_UPSTREAM_UPGRADE_URL environmental variable - --allow-download-upgrade-binaries - If set to true, this will enable auto-downloading of new binaries using the url provided in the `upgrade-info.json` Can be overridden with $DAEMON_ALLOW_BINARIES_DOWNLOAD environmental variable [possible values: true, false] - --enforce-download-checksum - If enabled nymvisor will require that a checksum is provided in the upgrade plan for the binary to be downloaded. If disabled, nymvisor will not require a checksum to be provided, but still check the checksum if one is provided. Can be overridden with $DAEMON_ENFORCE_DOWNLOAD_CHECKSUM environmental variable [possible values: true, false] - --restart-daemon-after-upgrade - If enabled, nymvisor will restart the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. Otherwise (if disabled), nymvisor will stop running after an upgrade and will require the system administrator to manually restart it. Note restart is only after the upgrade and does not auto-restart the subprocess after an error occurs. Can be overridden with $DAEMON_RESTART_AFTER_UPGRADE environmental variable [possible values: true, false] - --restart-daemon-on-failure - If enabled, nymvisor will restart the subprocess with the same command-line arguments and flags after it has crashed Can be overridden with $DAEMON_RESTART_ON_FAILURE environmental variable - --on-failure-daemon-restart-delay - If `restart_on_failure` is enabled, the following value defines the amount of time `nymvisor` shall wait before restarting the subprocess. Can be overridden with $DAEMON_FAILURE_RESTART_DELAY environmental variable - --max-daemon-startup-failures - Defines the maximum number of startup failures the subprocess can experience in a quick succession before no further restarts will be attempted and `nymvisor` will exit. Can be overridden with $DAEMON_MAX_STARTUP_FAILURES environmental variable - --startup-period-duration - Defines the length of time during which the subprocess is still considered to be in the startup phase when its failures are going to be considered in `max_startup_failures`. Can be overridden with $DAEMON_STARTUP_PERIOD_DURATION environmental variable - --daemon-shutdown-grace-period - Specifies the amount of time `nymvisor` is willing to wait for the subprocess to undergo graceful shutdown after receiving an interrupt (for either an upgrade or shutdown of the `nymvisor` itself) Once the time passes, a kill signal is going to be sent instead. Can be overridden with $DAEMON_SHUTDOWN_GRACE_PERIOD environmental variable - --daemon-backup-data-directory - Set custom backup directory for daemon data. If not set, the daemon's home directory will be used instead. Can be overridden with $DAEMON_BACKUP_DATA_DIRECTORY environmental variable - --unsafe-skip-backup - If enabled, `nymvisor` will perform upgrades directly without performing any backups. default: false Can be overridden with $DAEMON_UNSAFE_SKIP_BACKUP environmental variable - -o, --output - [default: text] [possible values: text, json] - -h, --help - Print help -``` diff --git a/documentation/autodoc/src/main.rs b/documentation/autodoc/src/main.rs index fda48a85f7..7f9513097c 100644 --- a/documentation/autodoc/src/main.rs +++ b/documentation/autodoc/src/main.rs @@ -127,7 +127,7 @@ fn main() -> io::Result<()> { writeln!( file, "# {} Binary Commands (Autogenerated)", - format!("`{}`", last_word.unwrap()) + format_args!("`{}`", last_word.unwrap()) )?; writeln!( file, @@ -152,7 +152,7 @@ fn main() -> io::Result<()> { writeln!( file, "# {} Binary Commands (Autogenerated)", - format!("`{}`", last_word.unwrap()) + format_args!("`{}`", last_word.unwrap()) )?; writeln!( file, @@ -190,9 +190,10 @@ fn execute_command_own_file(main_command: &str, subcommand: &str) -> io::Result< // this check is basically checking for the rare commands (rn just one) that start a process with no params // perhaps if this list grows we could just add a timeout and shunt the running and writing // into a thread with a timeout or something but for right now its fine / thats overkill - if get_last_word_from_filepath(main_command).unwrap() == "nym-node" && subcommand == "run" - || get_last_word_from_filepath(main_command).unwrap() == "nym-api" && subcommand == "run" - || get_last_word_from_filepath(main_command).unwrap() == "nymvisor" && subcommand == "run" + if (get_last_word_from_filepath(main_command).unwrap() == "nymvisor" + || get_last_word_from_filepath(main_command).unwrap() == "nym-api" + || get_last_word_from_filepath(main_command).unwrap() == "nym-node") + && subcommand == "run" { info!("SKIPPING {} {}", main_command, subcommand); } else { @@ -278,7 +279,7 @@ fn execute_command( ) -> io::Result<()> { // checking for the nym-cli subsubcommands if subsubcommand.is_some() { - writeln!(file, "\n### `{} {}`", subcommand, subsubcommand.unwrap())?; + writeln!(file, "\n## `{} {}`", subcommand, subsubcommand.unwrap())?; info!("executing {} {} --help ", main_command, subcommand); let output = Command::new(main_command) @@ -293,7 +294,7 @@ fn execute_command( } // just subcommands } else { - writeln!(file, "\n### `{}`", subcommand)?; + writeln!(file, "\n## `{}`", subcommand)?; // execute help let output = Command::new(main_command) diff --git a/documentation/docs/.env.example b/documentation/docs/.env.example new file mode 100644 index 0000000000..2ed3461d68 --- /dev/null +++ b/documentation/docs/.env.example @@ -0,0 +1 @@ +NEXT_PUBLIC_SITE_URL=http://localhost:3000 \ No newline at end of file diff --git a/documentation/docs/.gitignore b/documentation/docs/.gitignore index 46f9bd0984..515d7fce8d 100644 --- a/documentation/docs/.gitignore +++ b/documentation/docs/.gitignore @@ -4,3 +4,7 @@ out # the lock file will break Vercel because it may get committed from a machine with a different build architecture package-lock.json + +# local env files +.env*.local +.env \ No newline at end of file diff --git a/documentation/docs/components/api-table.tsx b/documentation/docs/components/api-table.tsx new file mode 100644 index 0000000000..236f04f828 --- /dev/null +++ b/documentation/docs/components/api-table.tsx @@ -0,0 +1,52 @@ +import React, { useState, useEffect } from "react"; + +export default function NymDealersAddresses({ + endpoint, +}: { + endpoint: string; +}) { + const [announceAddresses, setAnnounceAddresses] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await fetch(endpoint); + + if (!response.ok) { + throw new Error("Failed to fetch data"); + } + + const jsonData = await response.json(); + + const addresses = jsonData.data.dealers.map( + (dealer: any) => dealer.announce_address + ); + + setAnnounceAddresses(addresses); + setIsLoading(false); + } catch (error) { + setError(error instanceof Error ? error.message : "Unknown error"); + setIsLoading(false); + } + }; + + fetchData(); + }, [endpoint]); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error}
; + + return ( + + + {announceAddresses.map((address, index) => ( + + {address} + + ))} + +
+ ); +} diff --git a/documentation/docs/components/landing-page.tsx b/documentation/docs/components/landing-page.tsx index 87b9f29fff..dd6ae77db1 100644 --- a/documentation/docs/components/landing-page.tsx +++ b/documentation/docs/components/landing-page.tsx @@ -10,12 +10,6 @@ import networkDocs from "../public/images/landing/network-docs.png"; import developerDocs from "../public/images/landing/developer-docs.png"; import sdkDocs from "../public/images/landing/sdk-docs.png"; import operatorGuide from "../public/images/landing/operator-guide.png"; -import { t } from "nextra/dist/types-c8e621b7"; -// import networkDocs from "./images/network-docs.png"; -// import developerDocs from "./images/developer-docs.png"; -// import sdkDocs from "./images/sdk-docs.png"; -// import operatorGuide from "./images/operator-guide.png"; - export const LandingPage = () => { const theme = useTheme(); const isTablet = useMediaQuery(theme.breakpoints.up("md")); @@ -38,19 +32,25 @@ export const LandingPage = () => { }, { text: "Developer Portal", - description: - "Conceptual overview, clients, and tools for developers and integrations", + description: "Conceptual overview, clients, tools and SDKs", href: "/developers", - icon: networkDocs, - }, - { - text: "SDKs", - description: "Rust and Typescript SDK docs", - - href: "/developers/rust", icon: sdkDocs, }, + // { + // text: "SDKs", + // description: "Rust and Typescript SDK docs", + + // href: "/developers/rust", + // icon: sdkDocs, + // }, + { + text: "APIs", + description: "Interactive API specs for Nym infrastructure", + + href: "/apis/introduction", + icon: networkDocs, + }, ]; const shortenDescription = (description: string) => { diff --git a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/circulating-supply.md b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/circulating-supply.md index 0d7c399aa9..7f5aad7cd4 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/circulating-supply.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/circulating-supply.md @@ -1 +1 @@ -803_103_234 +804_560_131 diff --git a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/stake-saturation.md b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/stake-saturation.md index 8bc02c386e..b2ed9e28ef 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/stake-saturation.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/stake-saturation.md @@ -1 +1 @@ -1_016_987 +1_020_023 diff --git a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/staking-target.md b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/staking-target.md index af97e1d311..49bf2c6564 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/staking-target.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/staking-target.md @@ -1 +1 @@ -401_551_617 +402_280_065 diff --git a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/token-table.md b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/token-table.md index b1f5c4dea3..413c6d0177 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/token-table.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/token-table.md @@ -1,7 +1,7 @@ | **Item** | **Description** | **Amount in NYM** | |:-------------------|:------------------------------------------------------|--------------------:| | Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 | -| Mixmining Reserve | Tokens releasing for operators rewards | 196_896_265 | +| Mixmining Reserve | Tokens releasing for operators rewards | 195_439_368 | | Vesting Tokens | Tokens locked outside of cicrulation for future claim | 500 | -| Circulating Supply | Amount of unlocked tokens | 803_103_234 | -| Stake Saturation | Optimal size of node self-bond + delegation | 1_016_987 | +| Circulating Supply | Amount of unlocked tokens | 804_560_131 | +| Stake Saturation | Optimal size of node self-bond + delegation | 1_020_023 | diff --git a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md index a203dd666d..f3566e41d5 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md @@ -1 +1 @@ -Wednesday, November 20th 2024, 15:53:00 UTC +Monday, December 9th 2024, 19:43:12 UTC diff --git a/documentation/docs/components/outputs/command-outputs/nym-api-build-info-help.md b/documentation/docs/components/outputs/command-outputs/nym-api-build-info-help.md index 7ece0cff3a..db52a92b4e 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-api-build-info-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-api-build-info-help.md @@ -1,5 +1,4 @@ ```sh -2024-10-29T09:51:44.050179Z  INFO nym-api/src/main.rs:41: Starting nym api... Show build information of this binary Usage: nym-api build-info [OPTIONS] diff --git a/documentation/docs/components/outputs/command-outputs/nym-api-build-info.md b/documentation/docs/components/outputs/command-outputs/nym-api-build-info.md index 107dd29a8a..c7ef59a43c 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-api-build-info.md +++ b/documentation/docs/components/outputs/command-outputs/nym-api-build-info.md @@ -1,14 +1,13 @@ ```sh -2024-10-29T09:51:44.043480Z  INFO nym-api/src/main.rs:41: Starting nym api... Binary Name: nym-api -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 1.1.45 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable +Build Timestamp: 2024-12-10T11:37:07.839071360Z +Build Version: 1.1.46 +Commit SHA: 62045d76b32265f6a5e6358ab7aebbc827d47dcd +Commit Date: 2024-11-26T11:53:05.000000000+01:00 +Commit Branch: master +rustc Version: 1.84.0-nightly +rustc Channel: nightly cargo Profile: release ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-api-help.md b/documentation/docs/components/outputs/command-outputs/nym-api-help.md index ca6467eb19..1ba192c6e8 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-api-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-api-help.md @@ -1,5 +1,4 @@ ```sh -2024-11-20T15:53:01.702819Z  INFO nym-api/src/main.rs:40: Starting nym api... Usage: nym-api [OPTIONS] Commands: diff --git a/documentation/docs/components/outputs/command-outputs/nym-api-init-help.md b/documentation/docs/components/outputs/command-outputs/nym-api-init-help.md index 6427278bdf..7253758dc6 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-api-init-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-api-init-help.md @@ -1,5 +1,4 @@ ```sh -2024-10-29T09:51:44.027197Z  INFO nym-api/src/main.rs:41: Starting nym api... Initialise a Nym Api instance with persistent config.toml file Usage: nym-api init [OPTIONS] diff --git a/documentation/docs/components/outputs/command-outputs/nym-api-init.md b/documentation/docs/components/outputs/command-outputs/nym-api-init.md index 976a4ae91d..89b8486a06 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-api-init.md +++ b/documentation/docs/components/outputs/command-outputs/nym-api-init.md @@ -1,3 +1,3 @@ ```sh -2024-10-29T09:51:44.019255Z  INFO nym-api/src/main.rs:41: Starting nym api... +2024-11-06T12:18:46.199371Z  INFO nym-api/src/main.rs:41: Starting nym api... ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-client-build-info.md b/documentation/docs/components/outputs/command-outputs/nym-client-build-info.md index 8bf2f6fa1c..660d02d4a2 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-client-build-info.md +++ b/documentation/docs/components/outputs/command-outputs/nym-client-build-info.md @@ -1,13 +1,13 @@ ```sh Binary Name: nym-client -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 1.1.42 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable +Build Timestamp: 2024-12-10T11:37:07.839071360Z +Build Version: 1.1.44 +Commit SHA: 62045d76b32265f6a5e6358ab7aebbc827d47dcd +Commit Date: 2024-11-26T11:53:05.000000000+01:00 +Commit Branch: master +rustc Version: 1.84.0-nightly +rustc Channel: nightly cargo Profile: release ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-bonding-information.md b/documentation/docs/components/outputs/command-outputs/nym-node-bonding-information.md index b20c9dd9fb..5cda2291e2 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-bonding-information.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-bonding-information.md @@ -1,3 +1,3 @@ ```sh -2024-10-29T09:51:44.440498Z ERROR nym-node/src/config/upgrade_helpers.rs:16: Failed to finish upgrade - failed to load config file using path '/home/m/.nym/nym-nodes/default-nym-node/config/config.toml'. detailed message: No such file or directory (os error 2) +2024-11-06T12:18:46.723546Z ERROR nym-node/src/config/upgrade_helpers.rs:17: Failed to finish upgrade - failed to load config file using path '/home/m/.nym/nym-nodes/default-nym-node/config/config.toml'. detailed message: No such file or directory (os error 2) ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-build-info.md b/documentation/docs/components/outputs/command-outputs/nym-node-build-info.md index abc2bd416f..7ed28e4de5 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-build-info.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-build-info.md @@ -1,13 +1,13 @@ ```sh Binary Name: nym-node -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 1.1.9 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable +Build Timestamp: 2024-12-10T11:37:07.839071360Z +Build Version: 1.1.11 +Commit SHA: 62045d76b32265f6a5e6358ab7aebbc827d47dcd +Commit Date: 2024-11-26T11:53:05.000000000+01:00 +Commit Branch: master +rustc Version: 1.84.0-nightly +rustc Channel: nightly cargo Profile: release ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-node-details.md b/documentation/docs/components/outputs/command-outputs/nym-node-node-details.md index 768152ed3a..37077dda8f 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-node-details.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-node-details.md @@ -1,3 +1,3 @@ ```sh -2024-10-29T09:51:44.459681Z ERROR nym-node/src/config/upgrade_helpers.rs:16: Failed to finish upgrade - failed to load config file using path '/home/m/.nym/nym-nodes/default-nym-node/config/config.toml'. detailed message: No such file or directory (os error 2) +2024-11-06T12:18:46.742880Z ERROR nym-node/src/config/upgrade_helpers.rs:17: Failed to finish upgrade - failed to load config file using path '/home/m/.nym/nym-nodes/default-nym-node/config/config.toml'. detailed message: No such file or directory (os error 2) ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md index 32508afe64..29da832b97 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md @@ -54,6 +54,8 @@ Options: Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false] --wireguard-bind-address Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=] + --wireguard-private-ip + Private IP address of the wireguard gateway. default: `10.1.0.1` [env: NYMNODE_WG_IP=] --wireguard-announced-port Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] --wireguard-private-network-prefix diff --git a/documentation/docs/components/outputs/command-outputs/nym-socks5-client-build-info.md b/documentation/docs/components/outputs/command-outputs/nym-socks5-client-build-info.md index 6ae9f633fe..cb9ed56157 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-socks5-client-build-info.md +++ b/documentation/docs/components/outputs/command-outputs/nym-socks5-client-build-info.md @@ -1,13 +1,13 @@ ```sh Binary Name: nym-socks5-client -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 1.1.42 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable +Build Timestamp: 2024-12-10T11:37:07.839071360Z +Build Version: 1.1.44 +Commit SHA: 62045d76b32265f6a5e6358ab7aebbc827d47dcd +Commit Date: 2024-11-26T11:53:05.000000000+01:00 +Commit Branch: master +rustc Version: 1.84.0-nightly +rustc Channel: nightly cargo Profile: release ``` diff --git a/documentation/docs/components/outputs/command-outputs/nymvisor-build-info.md b/documentation/docs/components/outputs/command-outputs/nymvisor-build-info.md index 6db90dcec6..a373c4b930 100644 --- a/documentation/docs/components/outputs/command-outputs/nymvisor-build-info.md +++ b/documentation/docs/components/outputs/command-outputs/nymvisor-build-info.md @@ -1,13 +1,13 @@ ```sh Binary Name: nymvisor -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 0.1.8 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable +Build Timestamp: 2024-12-10T11:37:07.839071360Z +Build Version: 0.1.9 +Commit SHA: 62045d76b32265f6a5e6358ab7aebbc827d47dcd +Commit Date: 2024-11-26T11:53:05.000000000+01:00 +Commit Branch: master +rustc Version: 1.84.0-nightly +rustc Channel: nightly cargo Profile: release ``` diff --git a/documentation/docs/next-env.d.ts b/documentation/docs/next-env.d.ts index a4a7b3f5cf..4f11a03dc6 100644 --- a/documentation/docs/next-env.d.ts +++ b/documentation/docs/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/documentation/docs/package.json b/documentation/docs/package.json index 938ed95093..cd0a1371c0 100644 --- a/documentation/docs/package.json +++ b/documentation/docs/package.json @@ -15,6 +15,7 @@ "start": "next start" }, "dependencies": { + "@apidevtools/swagger-parser": "^10.1.0", "@coreui/coreui": "^5.1.2", "@coreui/react": "^5.4.0", "@cosmjs/amino": "^0.32.2", @@ -39,6 +40,7 @@ "@nymproject/contract-clients": ">=1.2.4-rc.2 || ^1", "@nymproject/mix-fetch-full-fat": ">=1.2.4-rc.2 || ^1", "@nymproject/sdk-full-fat": ">=1.2.4-rc.2 || ^1", + "@redocly/cli": "^1.25.15", "@types/mdx": "^2.0.13", "chain-registry": "^1.19.0", "cosmjs-types": "^0.9.0", diff --git a/documentation/docs/pages/_meta.json b/documentation/docs/pages/_meta.json index ba7bdce511..15f1b5e394 100644 --- a/documentation/docs/pages/_meta.json +++ b/documentation/docs/pages/_meta.json @@ -27,5 +27,9 @@ "operators": { "title": "Operators", "type": "page" + }, + "apis": { + "title": "APIs", + "type": "page" } } diff --git a/documentation/docs/pages/apis/_meta.json b/documentation/docs/pages/apis/_meta.json new file mode 100644 index 0000000000..616cc14334 --- /dev/null +++ b/documentation/docs/pages/apis/_meta.json @@ -0,0 +1,12 @@ +{ + "introduction": "Introduction", + "ns-api": "Node Status API", + "nym-api": "NymAPI Validator Sidecar", + "explorer-api": "Explorer API", + "cosmos-sdk-nyx": "Validator REST API", + "---": { + "type": "separator" + }, + "licensing": "Licensing", + "coc": "Coc" +} diff --git a/documentation/docs/pages/apis/cosmos-sdk-nyx.mdx b/documentation/docs/pages/apis/cosmos-sdk-nyx.mdx new file mode 100644 index 0000000000..b43138fd3e --- /dev/null +++ b/documentation/docs/pages/apis/cosmos-sdk-nyx.mdx @@ -0,0 +1,3 @@ +# Validator REST API + +Since the [Nyx validators](../../operators/nodes/validator-setup) are built with the Cosmos SDK, they by default expose a [REST API](https://docs.cosmos.network/api) which can be used to query the state of the chain. diff --git a/documentation/docs/pages/apis/cosmos-sdk-nyx/mainnet.mdx b/documentation/docs/pages/apis/cosmos-sdk-nyx/mainnet.mdx new file mode 100644 index 0000000000..72233bd16a --- /dev/null +++ b/documentation/docs/pages/apis/cosmos-sdk-nyx/mainnet.mdx @@ -0,0 +1,18 @@ +import { RedocStandalone } from 'redoc'; + +The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://api.nymtech.net/swagger/swagger.yaml](https://api.nymtech.net/swagger/swagger.yaml) which is also used to generate the Swagger docs deployed at [https://api.nymtech.net/swagger/](https://api.nymtech.net/swagger/). + +There is also an overview of other Validator endpoints at [https://cosmos.directory/nyx](https://cosmos.directory/nyx). + + diff --git a/documentation/docs/pages/apis/cosmos-sdk-nyx/sandbox.mdx b/documentation/docs/pages/apis/cosmos-sdk-nyx/sandbox.mdx new file mode 100644 index 0000000000..726ef0b51e --- /dev/null +++ b/documentation/docs/pages/apis/cosmos-sdk-nyx/sandbox.mdx @@ -0,0 +1,16 @@ +import { RedocStandalone } from 'redoc'; + +The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at []() which is also used to generate the Swagger docs deployed at [](). + + diff --git a/documentation/docs/pages/apis/explorer-api.mdx b/documentation/docs/pages/apis/explorer-api.mdx new file mode 100644 index 0000000000..0ab207360d --- /dev/null +++ b/documentation/docs/pages/apis/explorer-api.mdx @@ -0,0 +1,8 @@ + +# Explorer API + +The Explorer API is the backend for the [Mixnet Explorer](https://explorer.nymtech.net/). + +**This will soon be deprecated in favour of the [Node Status API](../ns-api).** + +The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/explorer). diff --git a/documentation/docs/pages/apis/explorer-api/mainnet.mdx b/documentation/docs/pages/apis/explorer-api/mainnet.mdx new file mode 100644 index 0000000000..83853f6684 --- /dev/null +++ b/documentation/docs/pages/apis/explorer-api/mainnet.mdx @@ -0,0 +1,17 @@ + +import { RedocStandalone } from 'redoc'; + +The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://explorer.nymtech.net/api/v1/openapi.json](https://explorer.nymtech.net/api/v1/openapi.json) which is also used to generate the Swagger docs deployed at [https://explorer.nymtech.net/api/swagger/index.html](https://explorer.nymtech.net/api/swagger/index.html). + + diff --git a/documentation/docs/pages/apis/explorer-api/sandbox.mdx b/documentation/docs/pages/apis/explorer-api/sandbox.mdx new file mode 100644 index 0000000000..4053d7cd18 --- /dev/null +++ b/documentation/docs/pages/apis/explorer-api/sandbox.mdx @@ -0,0 +1,16 @@ +import { RedocStandalone } from 'redoc'; + +The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://sandbox-explorer.nymtech.net/api/v1/openapi.json](https://sandbox-explorer.nymtech.net/api/v1/openapi.json) which is also used to generate the Swagger docs deployed at [https://sandbox-explorer.nymtech.net/api/swagger/index.html](https://sandbox-explorer.nymtech.net/api/swagger/index.html). + + diff --git a/documentation/docs/pages/apis/introduction.mdx b/documentation/docs/pages/apis/introduction.mdx new file mode 100644 index 0000000000..c2c521ba8c --- /dev/null +++ b/documentation/docs/pages/apis/introduction.mdx @@ -0,0 +1,5 @@ +# Introduction + +This site contains interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet. + +You can find links to the generated specs for each API on their respective pages as well as their different uses for operators and developers. diff --git a/documentation/docs/pages/apis/ns-api.mdx b/documentation/docs/pages/apis/ns-api.mdx new file mode 100644 index 0000000000..85d2ea4566 --- /dev/null +++ b/documentation/docs/pages/apis/ns-api.mdx @@ -0,0 +1,11 @@ +import { Callout } from 'nextra/components' + +# Node Status API + +The Node Status API contains information about the network, its topology, and the routing scores of all nodes within it. It offers broadly similar information to the experimental [Habourmaster frontend](https://harbourmaster.nymtech.net/) but is stable where the Harbourmaster is subject to sudden changes as we modify and experiment with how we scrape and present data about the Mixnet infrastructure. + + +People building applications or dashboards which requires information about nodes, their uptime, and their delegations should use this instead of Habourmaster. + + +The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/nym-node-status-api). In the future we will encourage developers to run their own instance of this API in order to distribute endpoints and query load. diff --git a/documentation/docs/pages/apis/ns-api/mainnet.mdx b/documentation/docs/pages/apis/ns-api/mainnet.mdx new file mode 100644 index 0000000000..d13f994c5e --- /dev/null +++ b/documentation/docs/pages/apis/ns-api/mainnet.mdx @@ -0,0 +1,16 @@ +import { RedocStandalone } from 'redoc'; + +The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json](https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://mainnet-node-status-api.nymtech.cc/swagger/](https://mainnet-node-status-api.nymtech.cc/swagger/). + + diff --git a/documentation/docs/pages/apis/ns-api/sandbox.mdx b/documentation/docs/pages/apis/ns-api/sandbox.mdx new file mode 100644 index 0000000000..aa553ea145 --- /dev/null +++ b/documentation/docs/pages/apis/ns-api/sandbox.mdx @@ -0,0 +1,16 @@ +import { RedocStandalone } from 'redoc'; + +The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://sandbox-node-status-api.nymte.ch/api-docs/openapi.json](https://sandbox-node-status-api.nymte.ch/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://sandbox-node-status-api.nymte.ch/swagger/](https://sandbox-node-status-api.nymte.ch/swagger/). + + diff --git a/documentation/docs/pages/apis/nym-api.mdx b/documentation/docs/pages/apis/nym-api.mdx new file mode 100644 index 0000000000..15be469fc4 --- /dev/null +++ b/documentation/docs/pages/apis/nym-api.mdx @@ -0,0 +1,5 @@ +# NymAPI + +The [NymAPI](../../operators/nodes/validator-setup/nym-api) is a sidecar binary operated by members of the Nym Validator set. Amongst other things, the API offers endpoints to query regarding circulating `NYM` supply, and global and ticketbook-specific [zk-nym](../../network/cryptography/zk-nym) data. This is all information contained in various smart contracts on the Nyx blockchain; the NymAPI caches this information periodically to make queries faster and more scalable. + +The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/nym-api). diff --git a/documentation/docs/pages/apis/nym-api/mainnet.mdx b/documentation/docs/pages/apis/nym-api/mainnet.mdx new file mode 100644 index 0000000000..426dc780cb --- /dev/null +++ b/documentation/docs/pages/apis/nym-api/mainnet.mdx @@ -0,0 +1,10 @@ +import APITable from 'components/api-table.tsx'; + +You can find the OpenAPI spec found at [https://validator.nymtech.net/api-docs/openapi.json](https://validator.nymtech.net/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://validator.nymtech.net/api/swagger/index.html](https://validator.nymtech.net/api/swagger/index.html). + +You can find the Swagger endpoints of other NymAPI instances at the following endpoints: + + +There is also an overview of endpoints at [https://cosmos.directory/nyx](https://cosmos.directory/nyx). + +> Interactive Redoc component coming soon diff --git a/documentation/docs/pages/developers/_meta.json b/documentation/docs/pages/developers/_meta.json index d0103c08a5..b3e5134275 100644 --- a/documentation/docs/pages/developers/_meta.json +++ b/documentation/docs/pages/developers/_meta.json @@ -5,6 +5,7 @@ "integrations": "Integration Options", "clients": "Clients", "tools": "Tools", + "nymvpncli": "Nym VPN CLI", "chain": "Interacting with Nyx", "--": { "type": "separator" diff --git a/documentation/docs/pages/developers/clients.mdx b/documentation/docs/pages/developers/clients.mdx index b507ecf7be..7b6f38491d 100644 --- a/documentation/docs/pages/developers/clients.mdx +++ b/documentation/docs/pages/developers/clients.mdx @@ -1,3 +1,7 @@ +--- +toc: float +--- + # Types of Nym clients At present, there are three Nym clients. These are built as standalone binaries when building our codebase, but are most easily accessed through one of our SDKs: diff --git a/documentation/docs/pages/developers/clients/socks5.mdx b/documentation/docs/pages/developers/clients/socks5.mdx index 9a49178094..08e40b6306 100644 --- a/documentation/docs/pages/developers/clients/socks5.mdx +++ b/documentation/docs/pages/developers/clients/socks5.mdx @@ -50,144 +50,8 @@ The `nym-socks5-client` allows you to do the following from your local machine: The `nym-node` then reassembles the original TCP stream using the packets' sequence numbers, and make the intended request. It will then chop up the response into Sphinx packets and send them back through the mixnet to your `nym-socks5-client`. The application will then receive its data, without even noticing that it wasn't talking to a "normal" SOCKS5 proxy! - ## Download or compile socks5 client If you are using OSX or a Debian-based operating system, you can download the `nym-socks5-client` binary from our [Github releases page](https://github.com/nymtech/nym/releases). If you are using a different operating system, or want to build from source, simply use `cargo build --release` from the root of the Nym monorepo. - -## Client setup -### Viewing command help - -You can check that your binaries are properly compiled with: - -``` -./nym-socks5-client --help -``` - -You can check the necessary parameters for the available commands by running: - -``` -./nym-client --help -``` - -### Initialising a new client instance -Before you can use the client, you need to initalise a new instance of it, which can be done with the following command: - -``` -./nym-socks5-client init --id docs-example --use-reply-surbs true --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf -``` - -The `--id` in the example above is a local identifier so that you can name your clients and keep track of them on your local system; it is **never** transmitted over the network. - -The `--use-reply-surbs` field denotes whether you wish to send [SURBs](../../network/concepts/anonymous-replies) along with your request. It defaults to `false`, we are explicitly setting it as `true`. It defaults to `false` for compatibility with versions of the pre-smoosh `nym-network-requester` binary which will soon be deprecated. - -The `--provider` field needs to be filled with the Nym address of an Exit Gateway that can make network requests on your behalf. You can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/gateways) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/). - -#### Choosing a Gateway -By default - as in the example above - your client will choose a random gateway to connect to. - -However, there are several options for choosing a gateway, if you do not want one that is randomly assigned to your client: -* If you wish to connect to a specific gateway, you can specify this with the `--gateway` flag when running `init`. -* You can also choose a gateway based on its location relative to your client. This can be done by appending the `--latency-based-selection` flag to your `init` command. This command means that to select a gateway, your client will: - * fetch a list of all availiable gateways - * send few ping messages to all of them, and measure response times. - * create a weighted distribution to randomly choose one, favouring ones with lower latency. - -You can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/gateways) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/). - -> Note this doesn't mean that your client will pick the closest gateway to you, but it will be far more likely to connect to gateway with a 20ms ping rather than 200ms - -### Configuring your client -When you initalise a client instance, a configuration directory will be generated and stored in `$HOME_DIR/.nym/socks5-clients//`. - -``` -tree $HOME//.nym/socks5-clients/docs-example -├── config -│   └── config.toml -└── data - ├── ack_key.pem - ├── credentials_database.db - ├── gateway_shared.pem - ├── persistent_reply_store.sqlite - ├── private_encryption.pem - ├── private_identity.pem - ├── public_encryption.pem - └── public_identity.pem - -``` - -The `config.toml` file contains client configuration options, while the two `pem` files contain client key information. - -The generated files contain the client name, public/private keypairs, and gateway address. The name `` in the example above is just a local identifier so that you can name your clients. - -#### Configuring your client for Docker -By default, the native client listens to host `127.0.0.1`. However this can be an issue if you wish to run a client in a Dockerized environment, where it can be convenenient to listen on a different host such as `0.0.0.0`. - -You can set this via the `--host` flag during either the `init` or `run` commands. - -Alternatively, a custom host can be set in the `config.toml` file under the `socket` section. If you do this, remember to restart your client process. - -### Running the socks5 client - -You can run the initialised client by doing this: - -``` -./nym-socks5-client run --id docs-example -``` - -## Automating your socks5 client with systemd - -Create a service file for the socks5 client at `/etc/systemd/system/nym-socks5-client.service`: - -```ini -[Unit] -Description=Nym Socks5 Client -StartLimitInterval=350 -StartLimitBurst=10 - -[Service] -User=nym # replace this with whatever user you wish -LimitNOFILE=65536 -ExecStart=/home/nym/nym-socks5-client run --id -KillSignal=SIGINT -Restart=on-failure -RestartSec=30 - -[Install] -WantedBy=multi-user.target -``` - -Now enable and start your socks5 client: - -``` -systemctl enable nym-socks5-client.service -systemctl start nym-socks5-client.service -# you can always check your socks5 client has succesfully started with: -systemctl status nym-socks5-client.service -``` - -## Using your Socks5 Client - -After completing the steps above, your local Socks5 Client will be listening on `localhost:1080` ready to proxy traffic to the Network Requester set as the `--provider` when initialising. - -When trying to connect your app, generally the proxy settings are found in `settings->advanced` or `settings->connection`. - -Here is an example of setting the proxy connecting in Blockstream Green: - -![Blockstream Green settings](/images/wallet-proxy-settings/blockstream-green.gif) - -Most wallets and other applications will work basically the same way: find the network proxy settings, enter the proxy url (host: **localhost**, port: **1080**). - -In some other applications, this might be written as **localhost:1080** if there's only one proxy entry field. - -## Useful Commands - -**no-banner** - -Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. - -**build-info** - -A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. diff --git a/documentation/docs/pages/developers/clients/socks5/_meta.json b/documentation/docs/pages/developers/clients/socks5/_meta.json new file mode 100644 index 0000000000..a050bc5042 --- /dev/null +++ b/documentation/docs/pages/developers/clients/socks5/_meta.json @@ -0,0 +1,6 @@ +{ + "setup": "Setup", + "usage": "Usage", + "config": "Config", + "commands": "Commands" +} diff --git a/documentation/docs/pages/developers/clients/socks5/commands.mdx b/documentation/docs/pages/developers/clients/socks5/commands.mdx index 1681dadc9b..2ecb2e7e49 100644 --- a/documentation/docs/pages/developers/clients/socks5/commands.mdx +++ b/documentation/docs/pages/developers/clients/socks5/commands.mdx @@ -25,7 +25,7 @@ Options: -V, --version Print version ``` -### `init` +## `init` ```sh Initialise a Nym client. Do this first! @@ -71,7 +71,7 @@ Options: Print help (see a summary with '-h') ``` -### `run` +## `run` ```sh Run the Nym client with provided configuration client optionally overriding set parameters @@ -107,9 +107,9 @@ Options: Print help (see a summary with '-h') ``` -### `import-credential` +## `import-credential` -### `list-gateways` +## `list-gateways` ```sh List all registered with gateways @@ -121,7 +121,7 @@ Options: -h, --help Print help ``` -### `add-gateway` +## `add-gateway` ```sh Add new gateway to this client @@ -138,7 +138,7 @@ Options: -h, --help Print help ``` -### `build-info` +## `build-info` ```sh Show build information of this binary @@ -152,18 +152,18 @@ Example output: ```sh Binary Name: nym-socks5-client -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 1.1.42 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable +Build Timestamp: 2024-12-10T11:37:07.839071360Z +Build Version: 1.1.44 +Commit SHA: 62045d76b32265f6a5e6358ab7aebbc827d47dcd +Commit Date: 2024-11-26T11:53:05.000000000+01:00 +Commit Branch: master +rustc Version: 1.84.0-nightly +rustc Channel: nightly cargo Profile: release ``` -### `completions` +## `completions` ```sh Generate shell completions @@ -176,7 +176,7 @@ Options: -h, --help Print help ``` -### `generate-fig-spec` +## `generate-fig-spec` ```sh Generate Fig specification diff --git a/documentation/docs/pages/developers/clients/socks5/config.mdx b/documentation/docs/pages/developers/clients/socks5/config.mdx new file mode 100644 index 0000000000..3c53d14da1 --- /dev/null +++ b/documentation/docs/pages/developers/clients/socks5/config.mdx @@ -0,0 +1,29 @@ +# Configuring your client +When you initalise a client instance, a configuration directory will be generated and stored in `$HOME_DIR/.nym/socks5-clients//`. + +``` +tree $HOME//.nym/socks5-clients/docs-example +├── config +│   └── config.toml +└── data + ├── ack_key.pem + ├── credentials_database.db + ├── gateway_shared.pem + ├── persistent_reply_store.sqlite + ├── private_encryption.pem + ├── private_identity.pem + ├── public_encryption.pem + └── public_identity.pem + +``` + +The `config.toml` file contains client configuration options, while the two `pem` files contain client key information. + +The generated files contain the client name, public/private keypairs, and gateway address. The name `` in the example above is just a local identifier so that you can name your clients. + +#### Configuring your client for Docker +By default, the native client listens to host `127.0.0.1`. However this can be an issue if you wish to run a client in a Dockerized environment, where it can be convenenient to listen on a different host such as `0.0.0.0`. + +You can set this via the `--host` flag during either the `init` or `run` commands. + +Alternatively, a custom host can be set in the `config.toml` file under the `socket` section. If you do this, remember to restart your client process. diff --git a/documentation/docs/pages/developers/clients/socks5/setup.mdx b/documentation/docs/pages/developers/clients/socks5/setup.mdx new file mode 100644 index 0000000000..1a02158aa0 --- /dev/null +++ b/documentation/docs/pages/developers/clients/socks5/setup.mdx @@ -0,0 +1,41 @@ +# Client setup +## Viewing command help + +You can check that your binaries are properly compiled with: + +``` +./nym-socks5-client --help +``` + +You can check the necessary parameters for the available commands by running: + +``` +./nym-client --help +``` + +## Initialising a new client instance +Before you can use the client, you need to initalise a new instance of it, which can be done with the following command: + +``` +./nym-socks5-client init --id docs-example --use-reply-surbs true --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf +``` + +The `--id` in the example above is a local identifier so that you can name your clients and keep track of them on your local system; it is **never** transmitted over the network. + +The `--use-reply-surbs` field denotes whether you wish to send [SURBs](../../network/concepts/anonymous-replies) along with your request. It defaults to `false`, we are explicitly setting it as `true`. It defaults to `false` for compatibility with versions of the pre-smoosh `nym-network-requester` binary which will soon be deprecated. + +The `--provider` field needs to be filled with the Nym address of an Exit Gateway that can make network requests on your behalf. You can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/gateways) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/). + +## Choosing a Gateway +By default - as in the example above - your client will choose a random gateway to connect to. + +However, there are several options for choosing a gateway, if you do not want one that is randomly assigned to your client: +* If you wish to connect to a specific gateway, you can specify this with the `--gateway` flag when running `init`. +* You can also choose a gateway based on its location relative to your client. This can be done by appending the `--latency-based-selection` flag to your `init` command. This command means that to select a gateway, your client will: + * fetch a list of all availiable gateways + * send few ping messages to all of them, and measure response times. + * create a weighted distribution to randomly choose one, favouring ones with lower latency. + +You can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/gateways) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/). + +> Note this doesn't mean that your client will pick the closest gateway to you, but it will be far more likely to connect to gateway with a 20ms ping rather than 200ms diff --git a/documentation/docs/pages/developers/clients/socks5/usage.mdx b/documentation/docs/pages/developers/clients/socks5/usage.mdx new file mode 100644 index 0000000000..be79556af8 --- /dev/null +++ b/documentation/docs/pages/developers/clients/socks5/usage.mdx @@ -0,0 +1,62 @@ +# Running the socks5 client + +You can run the initialised client by doing this: + +``` +./nym-socks5-client run --id docs-example +``` + +## Automating your socks5 client with systemd + +Create a service file for the socks5 client at `/etc/systemd/system/nym-socks5-client.service`: + +```ini +[Unit] +Description=Nym Socks5 Client +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nym # replace this with whatever user you wish +LimitNOFILE=65536 +ExecStart=/home/nym/nym-socks5-client run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +Now enable and start your socks5 client: + +``` +systemctl enable nym-socks5-client.service +systemctl start nym-socks5-client.service +# you can always check your socks5 client has succesfully started with: +systemctl status nym-socks5-client.service +``` + +## Using your Socks5 Client + +After completing the steps above, your local Socks5 Client will be listening on `localhost:1080` ready to proxy traffic to the Network Requester set as the `--provider` when initialising. + +When trying to connect your app, generally the proxy settings are found in `settings->advanced` or `settings->connection`. + +Here is an example of setting the proxy connecting in Blockstream Green: + +![Blockstream Green settings](/images/wallet-proxy-settings/blockstream-green.gif) + +Most wallets and other applications will work basically the same way: find the network proxy settings, enter the proxy url (host: **localhost**, port: **1080**). + +In some other applications, this might be written as **localhost:1080** if there's only one proxy entry field. + +## Useful Commands + +**no-banner** + +Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +**build-info** + +A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. diff --git a/documentation/docs/pages/developers/clients/websocket/_meta.json b/documentation/docs/pages/developers/clients/websocket/_meta.json new file mode 100644 index 0000000000..5e4171b09e --- /dev/null +++ b/documentation/docs/pages/developers/clients/websocket/_meta.json @@ -0,0 +1,7 @@ +{ + "setup": "Setup", + "usage": "Usage", + "config": "Config", + "examples": "Examples", + "commands": "Commands" +} diff --git a/documentation/docs/pages/developers/clients/websocket/commands.mdx b/documentation/docs/pages/developers/clients/websocket/commands.mdx index f2c7c7b0e4..e7a30285cb 100644 --- a/documentation/docs/pages/developers/clients/websocket/commands.mdx +++ b/documentation/docs/pages/developers/clients/websocket/commands.mdx @@ -25,7 +25,7 @@ Options: -V, --version Print version ``` -### `init` +## `init` ```sh Initialise a Nym client. Do this first! @@ -54,7 +54,7 @@ Options: Print help ``` -### `run` +## `run` ```sh Run the Nym client with provided configuration client optionally overriding set parameters @@ -77,9 +77,9 @@ Options: Print help ``` -### `import-credential` +## `import-credential` -### `list-gateways` +## `list-gateways` ```sh List all registered with gateways @@ -91,7 +91,7 @@ Options: -h, --help Print help ``` -### `switch-gateway` +## `switch-gateway` ```sh Change the currently active gateway. Note that you must have already registered with the new gateway! @@ -103,7 +103,7 @@ Options: -h, --help Print help ``` -### `build-info` +## `build-info` ```sh Show build information of this binary @@ -117,18 +117,18 @@ Example output: ```sh Binary Name: nym-client -Build Timestamp: 2024-10-29T09:48:31.988049207Z -Build Version: 1.1.42 -Commit SHA: 299552881810511273af13eb135297a4cf7a38de -Commit Date: 2024-10-29T10:48:07.000000000+01:00 -Commit Branch: max/new-docs-framework -rustc Version: 1.80.0 -rustc Channel: stable +Build Timestamp: 2024-12-10T11:37:07.839071360Z +Build Version: 1.1.44 +Commit SHA: 62045d76b32265f6a5e6358ab7aebbc827d47dcd +Commit Date: 2024-11-26T11:53:05.000000000+01:00 +Commit Branch: master +rustc Version: 1.84.0-nightly +rustc Channel: nightly cargo Profile: release ``` -### `completions` +## `completions` ```sh Generate shell completions @@ -141,7 +141,7 @@ Options: -h, --help Print help ``` -### `generate-fig-spec` +## `generate-fig-spec` ```sh Generate Fig specification diff --git a/documentation/docs/pages/developers/nymvpncli.mdx b/documentation/docs/pages/developers/nymvpncli.mdx new file mode 100644 index 0000000000..140f21549c --- /dev/null +++ b/documentation/docs/pages/developers/nymvpncli.mdx @@ -0,0 +1,281 @@ +import { Callout } from 'nextra/components' + +# Nym VPN CLI + +This is a short guide to setting up and using the `nym-vpnc` tool, which is used in conjunction with the `nym-vpnd` daemon. + + + These binaries have superceded the older `nym-vpn-cli` binary. This still operates for the moment as it is being used in testing scenarios but will go out of date quickly. + + +Download and run instructions for the GUIs can be found [here](https://nymvpn.com/en/download/linux). + +## Download & Extract Binary +Check the [release page](https://github.com/nymtech/nym-vpn-client/releases/) page for the latest release version and modify the instructions accordingly. These instructions use the latest as of the time of writing. +```sh +wget -q https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-core-v1.1.0-beta.3/nym-vpn-core-v1.1.0-beta.3_.tar.gz && +tar -xzf nym-vpn-core-v1.1.0-beta.3_.tar.gz && +cd nym-vpn-core-v1.1.0-beta.3_/ && +chmod u+x * +``` + +## Build from Source +### Prerequisites +All operating systems require both [Rust](https://www.rust-lang.org/tools/install) and [Go](https://go.dev/doc/install). + +**Arch specific packages:** +```sh +yay -S gcc make protobuf base-devel clang +``` + +**Ubuntu24 specific packages:** +```sh +apt install gcc make protobuf-compiler pkconfig libdbus-1-dev build-essential clang +``` + + + Older Debian/Ubuntu versions need to manually install `protobuf-compiler` >= v3.21.12 + + +### Clone & `make` +```sh +git clone https://github.com/nymtech/nym-vpn-client.git +cd nym-vpn-client/ +make +``` + +## Mnemonic Generation +Create an account at [nymvpn.com](nymvpn.com) to obtain your mnemonic. + +## Start the daemon +```sh +sudo ./PATH/TO/nym-vpnd +``` + +If you are running for the first time you will see the following: + +```sh +2024-12-11T11:03:58.202159Z INFO nym_vpnd::environment: Setting up environment by discovering the network: mainnet +2024-12-11T11:03:58.202205Z INFO nym_vpn_network_config::discovery: No discovery file found, writing default discovery file +2024-12-11T11:03:59.905505Z INFO nym_vpnd::command_interface::start: Starting command interface +2024-12-11T11:03:59.905660Z INFO nym_vpnd::service::vpn_service: Starting VPN service +2024-12-11T11:03:59.905879Z INFO nym_vpnd::command_interface::start: Starting socket listener on: /var/run/nym-vpn.sock +2024-12-11T11:03:59.906227Z INFO nym_vpn_account_controller::controller: Starting account controller +2024-12-11T11:03:59.906285Z INFO nym_vpn_account_controller::controller: Account controller: data directory: "/var/lib/nym-vpnd/mainnet" +2024-12-11T11:03:59.906313Z INFO nym_vpn_account_controller::controller: Account controller: credential mode: false +2024-12-11T11:03:59.913215Z INFO nym_vpnd::command_interface::listener: Removed previous command interface socket: "/var/run/nym-vpn.sock" +2024-12-11T11:03:59.977206Z INFO nym_vpnd::service::vpn_service: VPN service initialized successfully +2024-12-11T11:03:59.979246Z INFO nym_vpn_account_controller::controller: Account id: (unset) +2024-12-11T11:03:59.979265Z INFO nym_vpn_account_controller::controller: Device id: BZWA5MRnEvRYD8WWrH9KULdj2Q1uTssu6idjgWFae9dv +2024-12-11T11:03:59.979762Z INFO nym_vpn_account_controller::storage: Ticketbooks stored: 0 +2024-12-11T11:03:59.982125Z INFO nym_vpn_account_controller::controller: Received command: UpdateAccountState +2024-12-11T11:03:59.982181Z INFO nym_vpn_account_controller::shared_state: Setting mnemonic state to NotStored +2024-12-11T11:03:59.982200Z WARN nym_vpn_account_controller::commands: Returning error: NoAccountStored +2024-12-11T11:03:59.982218Z INFO nym_vpn_account_controller::controller: Received command: UpdateDeviceState +2024-12-11T11:03:59.982230Z INFO nym_vpn_account_controller::shared_state: Setting mnemonic state to NotStored +2024-12-11T11:03:59.982240Z WARN nym_vpn_account_controller::commands: Returning error: NoAccountStored +``` + +Ignore the `NoAccountStored` errors: these will disappear after the next step. **Leave the daemon running and run the following commands in another terminal window** or create an init file for `nym-vpnd`. + +## Run VPN +We have to first store the account we have created online: +```sh +./PATH/TO/nym-vpnc store-account --mnemonic "" +``` + +You will see this registration in the daemon logs: + +```sh +2024-12-11T11:04:31.918455Z INFO grpc_vpnd: ← StoreAccount () +2024-12-11T11:04:31.919296Z INFO nym_vpnd::service::vpn_service: Storing account +2024-12-11T11:04:31.919531Z INFO nym_vpn_store::mnemonic::on_disk: Storing mnemonic to: /var/lib/nym-vpnd/mainnet/mnemonic.json +2024-12-11T11:04:31.920327Z INFO nym_vpn_account_controller::controller: Received command: UpdateAccountState +2024-12-11T11:04:31.950720Z INFO nym_vpn_account_controller::shared_state: Setting mnemonic state to Stored { id: "n1nghj6qnmfww22tq6wyntnf709lr90qjem0uezz" } +2024-12-11T11:04:34.616249Z INFO nym_vpn_account_controller::shared_state: Setting account to Registered +2024-12-11T11:04:34.616363Z INFO nym_vpn_account_controller::shared_state: Setting account summary to AccountSummary { account: Active, subscription: Active, device_summary: DeviceSummary { active: 0, max: 10, remaining: 10 }, fair_usage: FairUsage { used_gb: None, limit_gb: None, resets_on_utc: Some("2025-01-09 15:43:37.223Z") } } +2024-12-11T11:04:34.981875Z INFO nym_vpn_account_controller::controller: Received command: RegisterDevice +2024-12-11T11:04:35.008575Z INFO register_device: nym_vpn_account_controller::shared_state: Setting device registration result to InProgress id=09876a3a +2024-12-11T11:04:35.008611Z INFO register_device: nym_vpn_account_controller::commands::register_device: Registering device: Device { identity_key: BZWA5MRnEvRYD8WWrH9KULdj2Q1uTssu6idjgWFae9dv } id=09876a3a +2024-12-11T11:04:36.765850Z INFO register_device: nym_vpn_account_controller::commands::register_device: Response: NymVpnDevice { + created_on_utc: "2024-12-11 11:04:36.432Z", + last_updated_utc: "2024-12-11 11:04:36.432Z", + device_identity_key: "BZWA5MRnEvRYD8WWrH9KULdj2Q1uTssu6idjgWFae9dv", + status: Active, +} id=09876a3a +2024-12-11T11:04:36.765998Z INFO register_device: nym_vpn_account_controller::commands::register_device: Device registered: BZWA5MRnEvRYD8WWrH9KULdj2Q1uTssu6idjgWFae9dv id=09876a3a +``` + +You can then connect `nym-vpnc` (in this case, with 2 hop wireguard mode enabled): +```sh +./PATH/TO/nym-vpnc connect --enable-two-hop +``` + +Which shows as such in the daemon logs: +```sh +2024-12-11T11:05:25.727784Z INFO grpc_vpnd: ← VpnConnect () +2024-12-11T11:05:25.728107Z INFO grpc_vpnd: nym_vpnd::command_interface::listener: Got connect request: Request { metadata: MetadataMap { headers: {"te": "trailers", "content-type": "application/grpc", "user-agent": "tonic/0.11.0"} }, message: ConnectRequest { entry: None, exit: None, dns: None, disable_routing: false, enable_two_hop: true, netstack: false, disable_poisson_rate: false, disable_background_cover_traffic: false, enable_credentials_mode: false, user_agent: Some(UserAgent { application: "nym-vpnc", version: "1.1.0-beta.3 (1.1.0-beta.3)", platform: "Manjaro Linux; Linux 24.2.0 Manjaro Linux; x86_64", git_commit: "59c0714f1dac1a2d8bf77f3d2705a5c9bb57a5be (59c0714f1dac1a2d8bf77f3d2705a5c9bb57a5be)" }), min_mixnode_performance: None, min_gateway_mixnet_performance: None, min_gateway_vpn_performance: None }, extensions: Extensions } +2024-12-11T11:05:25.728225Z INFO grpc_vpnd: nym_vpnd::command_interface::connection_handler: Starting VPN +2024-12-11T11:05:25.728430Z INFO nym_vpnd::service::vpn_service: Using entry point: None +2024-12-11T11:05:25.728450Z INFO nym_vpnd::service::vpn_service: Using exit point: None +2024-12-11T11:05:25.728468Z INFO nym_vpnd::service::vpn_service: Using options: ConnectOptions { dns: None, disable_routing: false, enable_two_hop: true, netstack: false, disable_poisson_rate: false, disable_background_cover_traffic: true, enable_credentials_mode: false, min_mixnode_performance: None, min_gateway_mixnet_performance: None, min_gateway_vpn_performance: None } +2024-12-11T11:05:25.729112Z INFO nym_vpnd::service::config: Config file updated at "/etc/nym/mainnet/nym-vpnd.toml" +2024-12-11T11:05:25.729161Z INFO nym_vpnd::service::vpn_service: Using config: entry point: Random, exit point: Random +2024-12-11T11:05:25.729611Z INFO nym_vpnd::service::vpn_service: Tunnel event: Connecting +2024-12-11T11:05:25.730108Z INFO nym_gateway_directory::gateway_client: Fetching gateways from nym-vpn-api... +2024-12-11T11:05:26.387699Z INFO nym_vpn_lib::tunnel_state_machine::tunnel::gateway_selector: Found 113 entry gateways +2024-12-11T11:05:26.387744Z INFO nym_vpn_lib::tunnel_state_machine::tunnel::gateway_selector: Found 113 exit gateways +2024-12-11T11:05:26.387752Z INFO nym_gateway_directory::entries::exit_point: Selecting a random exit gateway +2024-12-11T11:05:26.387794Z INFO nym_vpn_lib::tunnel_state_machine::tunnel::gateway_selector: Using entry gateway: CcYinhLeFU8n6xs78FG6Rz3wvosGTCU2hLB1CZyfkMVe, location: IN, performance: 96% +2024-12-11T11:05:26.387814Z INFO nym_vpn_lib::tunnel_state_machine::tunnel::gateway_se +lector: Using exit gateway: Atcji22Wnfwi6nEkGC5BmgbqNPLYdhx5r4NxTqXAzFeq, location: GB, performance: 99% +2024-12-11T11:05:26.387852Z INFO nym_vpn_lib::tunnel_state_machine::tunnel::gateway_selector: Using exit router address 3MJSnmUeH54a7DJ8C4C8oZPkCjtENSfwcMLJ39zUk9Ys.59h9HKGTM4MPXVJRDfaJYFg1aoAdeBGjLHMFxQ6fBsfF@Atcji22Wnfwi6nEkGC5BmgbqNPLYdhx5r4NxTqXAzFeq +2024-12-11T11:05:26.388144Z INFO nym_vpn_lib::mixnet::connect: mixnet client poisson rate limiting: disabled +2024-12-11T11:05:26.388154Z INFO nym_vpn_lib::mixnet::connect: mixnet client background loop cover traffic stream: disabled +2024-12-11T11:05:26.388158Z INFO nym_vpn_lib::mixnet::connect: mixnet client minimum mixnode performance: 50 +2024-12-11T11:05:26.388163Z INFO nym_vpn_lib::mixnet::connect: mixnet client minimum gateway performance: 50 +2024-12-11T11:05:26.388434Z INFO nym_client_core::client::base_client::non_wasm_helpers: loading existing surb database +2024-12-11T11:05:26.391135Z INFO nym_client_core_surb_storage::backend::fs_backend::manager: Database migration finished! +2024-12-11T11:05:26.503966Z INFO nym_client_core::init::helpers: nym-api reports 195 valid gateways +2024-12-11T11:05:27.888231Z INFO nym_client_core::client::base_client: Starting nym client +2024-12-11T11:05:27.894924Z INFO nym_client_core::client::base_client: Starting statistics control... +2024-12-11T11:05:27.895105Z INFO nym_client_core::client::base_client: Obtaining initial network topology +2024-12-11T11:05:28.047235Z INFO nym_client_core::client::base_client: Starting topology refresher... +2024-12-11T11:05:29.024114Z INFO perform_initial_authentication: nym_gateway_client::bandwidth: remaining bandwidth: 0.00 B gateway=CcYinhLeFU8n6xs78FG6Rz3wvosGTCU2hLB1CZyfkMVe gateway_address=wss://gateway4.lunardao.net:9001/ +2024-12-11T11:05:29.024375Z INFO nym_gateway_client::client: Claiming more bandwidth with existing credentials. Stop the process now if you don't want that to happen. +2024-12-11T11:05:29.024413Z WARN nym_gateway_client::client: Not enough bandwidth. Trying to get more bandwidth, this might take a while +2024-12-11T11:05:29.024430Z INFO nym_gateway_client::client: The client is running in disabled credentials mode - attempting to claim bandwidth without a credential +2024-12-11T11:05:29.171514Z INFO nym_gateway_client::client: managed to claim testnet bandwidth +2024-12-11T11:05:29.175555Z INFO nym_client_core::client::base_client: Starting received messages buffer controller... +2024-12-11T11:05:29.175648Z INFO nym_client_core::client::base_client: Starting mix traffic controller... +2024-12-11T11:05:29.175698Z INFO nym_client_core::client::base_client: Starting real traffic stream... +2024-12-11T11:05:29.176182Z INFO nym_task::manager: Starting status message listener +2024-12-11T11:05:29.189388Z INFO nym_vpn_lib::bandwidth_controller: Registering with wireguard gateway +2024-12-11T11:05:29.189449Z INFO nym_gateway_directory::gateway_client: Fetching gateway ip from nym-vpn-api... +2024-12-11T11:05:32.373749Z INFO nym_vpn_lib::bandwidth_controller: Registering with wireguard gateway +2024-12-11T11:05:32.373860Z INFO nym_gateway_directory::gateway_client: Fetching gateway ip from nym-vpn-api... +2024-12-11T11:05:36.675679Z INFO nym_vpn_lib::tunnel_state_machine::tunnel_monitor: Created entry tun device: tun0 +2024-12-11T11:05:36.679774Z INFO nym_vpn_lib::tunnel_state_machine::tunnel_monitor: Created exit tun device: tun1 +2024-12-11T11:05:36.681435Z INFO nym_dns: Setting DNS servers: Tunnel DNS: {1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001} Non-tunnel DNS: {} +2024-12-11T11:05:36.690464Z INFO nym_vpnd::service::vpn_service: Tunnel event: Connecting WireGuard tunnel with entry 213.210.21.111:51822 and exit 45.140.167.83:51822 +2024-12-11T11:05:36.690577Z INFO nym_vpnd::service::vpn_service: Tunnel event: Connected WireGuard tunnel with entry 213.210.21.111:51822 and exit 45.140.167.83:51822 +2024-12-11T11:05:37.734615Z INFO nym_wg_gateway_client: Remaining wireguard bandwidth with gateway CcYinhLeFU8n6xs78FG6Rz3wvosGTCU2hLB1CZyfkMVe for today: 256000.00 MB +2024-12-11T11:05:39.240143Z INFO nym_wg_gateway_client: Remaining wireguard bandwidth with gateway Atcji22Wnfwi6nEkGC5BmgbqNPLYdhx5r4NxTqXAzFeq for today: 256000.00 MB +``` + +You should see the `Remaining wireguard bandwidth` decrease as you use your allowance. + +There are a lot of configuration options available to you regarding how to connect: + +```sh +❯ ./PATH/TO/nym-vpnc connect --help +Connect to the Nym network + +Usage: nym-vpnc connect [OPTIONS] + +Options: + --entry-gateway-id + Mixnet public ID of the entry gateway + --entry-gateway-country + Auto-select entry gateway by country ISO + --entry-gateway-low-latency + Auto-select entry gateway by latency + --entry-gateway-random + Auto-select entry gateway randomly + --exit-router-address + Mixnet recipient address + --exit-gateway-id + Mixnet public ID of the exit gateway + --exit-gateway-country + Auto-select exit gateway by country ISO + --exit-gateway-random + Auto-select exit gateway randomly + --dns + Set the IP address of the DNS server to use + --disable-routing + Disable routing all traffic through the nym TUN device. When the flag is set, + the nym TUN device will be created, but to route traffic through it you will + need to do it manually, e.g. ping -Itun0 + --enable-two-hop + Enable two-hop wireguard traffic. This means that traffic jumps directly from + entry gateway to exit gateway using Wireguard protocol + -w, --wait-until-connected + Blocks until the connection is established or failed + --netstack + Use netstack based implementation for two-hop wireguard + --enable-credentials-mode + Enable credentials mode + --min-gateway-mixnet-performance + An integer between 0 and 100 representing the minimum gateway performance + required to consider a gateway for routing traffic + --min-gateway-vpn-performance + An integer between 0 and 100 representing the minimum gateway performance + required to consider a gateway for routing traffic + -h, --help + Print help +``` + +## Command Reference +```sh +❯ ./PATH/TO/nym-vpnc --help +NymVPN commandline client + +Usage: nym-vpnc [OPTIONS] + +Commands: + connect Connect to the Nym network + disconnect Disconnect from the Nym network + status Get the current status of the connection + info Get info about the current client. Things like version and + network details + set-network Set the network to be used. This requires a restart of the + daemon (`nym-vpnd`) + store-account Store the account recovery phrase + is-account-stored Check if the account is stored + forget-account Forget the stored account. This removes the stores recovery + phrase, device and mixnet keys, stored local credentials, etc + get-account-id Get the account ID + get-account-state Get the current account state + get-account-links Get URLs for managing your nym-vpn account + get-device-id Get the device ID + list-entry-gateways List the set of entry gateways for mixnet mode + list-exit-gateways List the set of exit gateways for mixnet mode + list-vpn-gateways List the set of entry and exit gateways for dVPN mode + list-entry-countries List the set of countries with available entry gateways for + mixnet mode + list-exit-countries List the set of countries with available exit gateways for + mixnet mode + list-vpn-countries List the set of countries with available entry and exit + gateways for dVPN mode + help Print this message or the help of the given subcommand(s) + +Options: + --http Use HTTP instead of socket file for IPC with the daemon + --verbose + -h, --help Print help + -V, --version Print version +``` + +```sh +❯ ./PATH/TO/nym-vpnd --help +NymVPN daemon + +Usage: nym-vpnd [OPTIONS] + +Options: + -c, --config-env-file + Path pointing to an env file describing the network + --enable-http-listener + + --disable-socket-listener + + --run-as-service + + -h, --help + Print help + -V, --version + Print version +``` + +## Default Config Directories +Configurations are stored in `/etc/nym`. State stored between runs (keys, mnemonic, etc) are stored in `/var/lib/nym-vpnd`. diff --git a/documentation/docs/pages/developers/tools/nym-cli/commands.mdx b/documentation/docs/pages/developers/tools/nym-cli/commands.mdx index 1117807a5a..9e15fc4eb3 100644 --- a/documentation/docs/pages/developers/tools/nym-cli/commands.mdx +++ b/documentation/docs/pages/developers/tools/nym-cli/commands.mdx @@ -67,7 +67,7 @@ Options: Print help ``` -### `account create` +## `account create` ```sh Create a new mnemonic - note, this account does not appear on the chain until the account id is used in a transaction @@ -92,7 +92,7 @@ Options: Print help ``` -### `account balance` +## `account balance` ```sh Gets the balance of an account @@ -124,7 +124,7 @@ Options: Print help ``` -### `account pub-key` +## `account pub-key` ```sh Gets the public key of an account @@ -152,7 +152,7 @@ Options: Print help ``` -### `account send` +## `account send` ```sh Sends tokens to another account @@ -183,7 +183,7 @@ Options: Print help ``` -### `account send-multiple` +## `account send-multiple` ```sh Batch multiple token sends @@ -241,7 +241,7 @@ Options: Print help ``` -### `signature sign` +## `signature sign` ```sh Sign a message @@ -267,7 +267,7 @@ Options: Print help ``` -### `signature verify` +## `signature verify` ```sh Verify a message @@ -329,7 +329,7 @@ Options: Print help ``` -### `ecash issue-ticket-book` +## `ecash issue-ticket-book` ```sh Usage: nym-cli ecash issue-ticket-book [OPTIONS] <--client-config |--output-file > @@ -366,7 +366,7 @@ Options: Print help ``` -### `ecash recover-ticket-book` +## `ecash recover-ticket-book` ```sh Usage: nym-cli ecash recover-ticket-book [OPTIONS] --client-config @@ -389,7 +389,7 @@ Options: Print help ``` -### `ecash import-ticket-book` +## `ecash import-ticket-book` ```sh Usage: nym-cli ecash import-ticket-book [OPTIONS] --credentials-store <--credential-data |--credential-path > <--standalone|--full> @@ -422,13 +422,13 @@ Options: ## `coconut` -### `coconut generate-freepass` +## `coconut generate-freepass` -### `coconut issue-credentials` +## `coconut issue-credentials` -### `coconut recover-credentials` +## `coconut recover-credentials` -### `coconut import-credential` +## `coconut import-credential` ## `block` ```sh @@ -460,7 +460,7 @@ Options: Print help ``` -### `block get` +## `block get` ```sh Gets a block's details and prints as JSON @@ -486,7 +486,7 @@ Options: Print help ``` -### `block time` +## `block time` ```sh Gets the block time at a height @@ -512,7 +512,7 @@ Options: Print help ``` -### `block current-height` +## `block current-height` ```sh Gets the current block height @@ -568,7 +568,7 @@ Options: Print help ``` -### `cosmwasm upload` +## `cosmwasm upload` ```sh Upload a smart contract WASM blob @@ -595,7 +595,7 @@ Options: Print help ``` -### `cosmwasm init` +## `cosmwasm init` ```sh Init a WASM smart contract @@ -633,7 +633,7 @@ Options: Print help ``` -### `cosmwasm generate-init-message` +## `cosmwasm generate-init-message` ```sh Generate an instantiate message @@ -665,7 +665,7 @@ Options: Print help ``` -### `cosmwasm migrate` +## `cosmwasm migrate` ```sh Migrate a WASM smart contract @@ -697,7 +697,7 @@ Options: Print help ``` -### `cosmwasm execute` +## `cosmwasm execute` ```sh Execute a WASM smart contract method @@ -758,7 +758,7 @@ Options: Print help ``` -### `tx get` +## `tx get` ```sh Get a transaction by hash or block height @@ -784,7 +784,7 @@ Options: Print help ``` -### `tx query` +## `tx query` ```sh Query for transactions @@ -841,7 +841,7 @@ Options: Print help ``` -### `vesting-schedule create` +## `vesting-schedule create` ```sh Creates a vesting schedule @@ -878,7 +878,7 @@ Options: Print help ``` -### `vesting-schedule query` +## `vesting-schedule query` ```sh Query for vesting schedule @@ -904,7 +904,7 @@ Options: Print help ``` -### `vesting-schedule vested-balance` +## `vesting-schedule vested-balance` ```sh Get the amount that has vested and is free for withdrawal, delegation or bonding @@ -930,7 +930,7 @@ Options: Print help ``` -### `vesting-schedule withdraw-vested` +## `vesting-schedule withdraw-vested` ```sh Withdraw vested tokens (note: the available amount excludes anything delegated or bonded before or after vesting) @@ -986,7 +986,7 @@ Options: Print help ``` -### `mixnet query` +## `mixnet query` ```sh Query the mixnet directory @@ -1015,7 +1015,7 @@ Options: Print help ``` -### `mixnet delegators` +## `mixnet delegators` ```sh Manage your delegations @@ -1050,7 +1050,7 @@ Options: Print help ``` -### `mixnet operators` +## `mixnet operators` ```sh Manage a mixnode or gateway you operate @@ -1104,4 +1104,4 @@ Options: Print help ``` -### `generate-fig ` +## `generate-fig ` diff --git a/documentation/docs/pages/network/concepts/mixing.mdx b/documentation/docs/pages/network/concepts/mixing.mdx index 387480a8b7..7027182d14 100644 --- a/documentation/docs/pages/network/concepts/mixing.mdx +++ b/documentation/docs/pages/network/concepts/mixing.mdx @@ -1,3 +1,8 @@ +--- +title: Packet Mixing +description: Mixnets are networks of nodes that route traffic in a way that makes it untraceable +--- + # Packet Mixing > Continuous-time mixing strategies ... delay each message independently, forwarding it to its next destination once a specified delay has timed out. The aggregate effect of independently delaying each message is an output sequence of messages that is randomly reordered with respect to the input sequence. diff --git a/documentation/docs/pages/network/cryptography/zk-nym/double-spend-prot.mdx b/documentation/docs/pages/network/cryptography/zk-nym/double-spend-prot.mdx index 05a5c86423..6da843967b 100644 --- a/documentation/docs/pages/network/cryptography/zk-nym/double-spend-prot.mdx +++ b/documentation/docs/pages/network/cryptography/zk-nym/double-spend-prot.mdx @@ -17,13 +17,13 @@ However, the offline approach introduces certain limitations. - Any potential repercussions against double spenders can only be implemented once the user requests a new credential for their zk-nym Generator (aka they have to 'top up' and buy more bandwidth allowance), assuming they haven't altered their identifier (the Bech32 address). An exploitable scenario arises from these limitations: -- A malicious user purchases bandwidth and aggregates a valid zk-nym credential in the standard way, worth $10 of crypto/fiat. Subsequently, the malicious user proceeds to sell the credential to 100 users for $1 each, allowing each user to generate zk-nym credits of 100MB from this **valid** credential. Under the offline approach, entry nodes forego double-spending checks; so long as the clients all used different ingress Gateways, all 100 users could access the network without obtaining a subscription. As bandwidth consumption is tracked locally between client and ingress node, and each zk-nym credit is rerandomised, there is no way that ingress Gateways would know that the zk-credential used by the client has been shared with other parties. This loophole highlights the need for stringent measures to counter such potential abuses within the system, without creating either speed bottlenecks (in the case of the Online model) or impacting the anonymity of the system. We can, however, mitigate this problem without doing either of these things. +- A malicious user purchases bandwidth and aggregates a valid zk-nym credential in the standard way, worth $10 of crypto/fiat. Subsequently, the malicious user proceeds to sell the credential to 100 users for $1 each, allowing each user to generate zk-nym tickets of 100MB from this **valid** credential. Under the offline approach, entry nodes forego double-spending checks; so long as the clients all used different ingress Gateways, all 100 users could access the network without obtaining a subscription. As bandwidth consumption is tracked locally between client and ingress node, and each zk-nym ticket is rerandomised, there is no way that ingress Gateways would know that the zk-credential used by the client has been shared with other parties. This loophole highlights the need for stringent measures to counter such potential abuses within the system, without creating either speed bottlenecks (in the case of the Online model) or impacting the anonymity of the system. We can, however, mitigate this problem without doing either of these things. ## Solution to Offline Double Spending To efficiently prevent the fraudulent use of tickets within the Nym network, a two-tiered solution is in place that combines (1) the immediate detection of double-spending attempts at the level of individuals ingress Gateways and (2) subsequent identification and blacklisting of offending clients at the Quorum level. ### Entry Node Implementation: Real-Time Ticket Unspending Validation -Each spent zk-nym credit contains as an attribute a unique serial number, which is revealed in plaintext to the respective ingress Gateway. Each Gateway has a copy of a [Bloom Filter](https://www.geeksforgeeks.org/bloom-filters-introduction-and-python-implementation/) - on receiving a credit, it will check against its copy of a local database to check whether this serial number has already been seen. If so, it rejects the credit as being double-spent and the client's connection request is rejected. If not, it will add the serial number to its local DB. +Each spent zk-nym ticket contains as an attribute a unique serial number, which is revealed in plaintext to the respective ingress Gateway. Each Gateway has a copy of a [Bloom Filter](https://www.geeksforgeeks.org/bloom-filters-introduction-and-python-implementation/) - on receiving a ticket, it will check against its copy of a local database to check whether this serial number has already been seen. If so, it rejects the ticket as being double-spent and the client's connection request is rejected. If not, it will add the serial number to its local DB. > Since each time a zk-nym credential is rerandomised its serial number is changed, the serial number being shared in no way identifies a client or user. @@ -32,6 +32,6 @@ Each Gateway will periodically share their serial numbers with the Quorum and re > Crucially, ingress Gateways refrain from extensive computations to identify the original ticket owner, and avoids broadcasting information about the double-spending attempt to other ingress Gateways. The entry node is also not involved in any global blacklisting process of the clients. The sole purpose of this check is to swiftly identify any attempts at double-spending and add the seen ticket's serial number to the local DB cache. ### Nym-API Implementation: Blacklisting and Penalties for Double-Spenders -All Gateways periodically forward the collected credits to the Quorum, enabling them to pinpoint and blacklist any clients who double spend. Upon receiving the credits, the Quorum appends all the incoming serial numbers to the global list of spend zk-nym serial numbers and proceed with the identification process for any malicious users engaging in double-spending. +All Gateways periodically forward the collected tickets to the Quorum, enabling them to pinpoint and blacklist any clients who double spend. Upon receiving the tickets, the Quorum appends all the incoming serial numbers to the global list of spend zk-nym serial numbers and proceed with the identification process for any malicious users engaging in double-spending. This identification phase involves looking for instances of double spending, identifying the id of the double-spending client, and blacklisting this client by its id. Subsequently, when this client requests a new credential, their plaintext public identifier is included in the request. The Quorum then checks if this identifier is blacklisted. If it is, a new credential is not issued. Furthermore, since the PSCs are only attainable after depositing NYM as payment, the Quorum has the authority to withhold the deposited NYMs as a punitive measure for any detected instances of double-spending. diff --git a/documentation/docs/pages/network/cryptography/zk-nym/rerandomise.mdx b/documentation/docs/pages/network/cryptography/zk-nym/rerandomise.mdx index 271fc17bf0..3b4a296eff 100644 --- a/documentation/docs/pages/network/cryptography/zk-nym/rerandomise.mdx +++ b/documentation/docs/pages/network/cryptography/zk-nym/rerandomise.mdx @@ -2,23 +2,20 @@ import { Callout } from 'nextra/components' # Rerandomisation & Incremental Spend -Each zk-nym credit will not be valid for the entire amount of data that the credential aggregated from the PSCs is; if the aggregated credential is worth (e.g.) 10GB of Mixnet data, each credit will be worth far less (e.g. 100MB). This amount will be globally uniform in order to avoid situations where differently sized credits allow for patterns to emerge. +Each ticket will not be valid for the entire amount of data that the ticketbook aggregated from the PSCs is; if the aggregated ticketbook is worth (e.g.) 10GB of Mixnet data, each ticket will be worth far less (e.g. 100MB). This amount will be globally uniform in order to avoid situations where differently sized tickets allow for patterns to emerge. The functionality included in the following code block examples were added to the [nym-cli tool](../tools/nym-cli.md) for illustrative purposes only: this is not necessarily how credentials will be accessed in the future. - **Furthermore, the `nym-cli` uses the words 'tickets' in place of 'credits' and 'ticketbook' in place of 'aggregated credential': this is wording that we are moving away from now in place of the simpler credential/credit.** This will be updated in the `nym-cli` tool in a future release. - - The numbers used in this high level overview are for illustration purposes only. The figures used in production will potentially vary. Note that individual zkNym sizes will be uniform across the Network. + The numbers used in this high level overview are for illustration purposes only. The figures used in production will potentially vary. Note that individual ticket sizes will be uniform across the Network. - -## Why not spend the entire credential at once? +## Why a 'ticketbook', not individual 'tickets', and why not spend them all at once? This is to account for the need for a client to change their ingress Gateway, either because the Gateway itself has gone down / is not offering the required bandwidth, or because a user might simply want to split their traffic across multiple Gateways for extra privacy. -This means that clients are not tied to particular Gateways they have 'spent' their entire subscription amount with; if the ingress Gateway goes down, or the client simply wishes to use another ingress Gateway, the user has multiple other zk-nym credits they can use that account for their remaining purchased bandwidth. +This means that clients are not tied to particular Gateways they have 'spent' their entire subscription amount with; if the ingress Gateway goes down, or the client simply wishes to use another ingress Gateway, the user has multiple other tickets they can use that account for their remaining purchased bandwidth. -Going back to the `nym-cli` tool to illustrate this; we can generate multiple unlinkable credits ('tickets' on this command output) from a single aggregated credential ('ticketbook' below): +Going back to the `nym-cli` tool to illustrate this; we can generate multiple unlinkable tickets from a single ticketbook aggregated from PSCs: ```sh ❯ ./nym-cli ecash generate-ticket --credential-storage storage.db --provider 6qidVK21zpHD298jdDa1RRpbRozP29ENVyqcSbm6hQrG --full diff --git a/documentation/docs/pages/network/cryptography/zk-nym/unlinkability.mdx b/documentation/docs/pages/network/cryptography/zk-nym/unlinkability.mdx index 90db009613..e6554299b6 100644 --- a/documentation/docs/pages/network/cryptography/zk-nym/unlinkability.mdx +++ b/documentation/docs/pages/network/cryptography/zk-nym/unlinkability.mdx @@ -1,14 +1,12 @@ import { Callout } from 'nextra/components' # Unlinkability -Each time a credential is requested by an ingress Gateway to prove that a client has purchased data to send through the Mixnet, instead of sending that credential, the Requester's device will produce a zk-nym credit. This is a rereandomised value that is able to be verified as being legitimate (in that it was created by a valid root credential) but **not linked to any other credits**, either previously generated or to be generated in the future. This feature also allows for a credential to be split across multiple ingress Gateways / connections and [incrementally spent](./rerandomise.md) over time. +Each time a credential is requested by an ingress Gateway to prove that a client has purchased data to send through the Mixnet the Requester's device will produce a ticket. This is a rereandomised value that is able to be verified as being legitimate (in that it was created by a valid root ticketbook) but **not linked to any other tickets**, either previously generated or to be generated in the future. This feature also allows for a single ticketbook to allow access to be split across multiple ingress Gateways / connections and [incrementally spent](./rerandomise.md) over time. The functionality included in the following code block examples were added to the [nym-cli tool](../tools/nym-cli.md) for illustrative purposes only: this is not necessarily how credentials will be accessed in the future. -**Furthermore, the `nym-cli` uses the words 'tickets' in place of 'credits' and 'ticketbook' in place of 'aggregated credential': this is wording that we are moving away from now in place of the simpler credential/credit.** This will be updated in the `nym-cli` tool in a future release. - The numbers used in this high level overview are for illustration purposes only. The figures used in production will potentially vary. Note that individual zkNym sizes will be uniform across the Network. @@ -24,7 +22,7 @@ PAYMENT FOR TICKET 3: VfZAuVRRHekQYMvFevNAZmPPuwMAfEhTBY8TXatBysbrNXAg8euEGPpJvdbhNfQSznBb9nRSeBUSVoNTToSA6Uj5dXmJ7oE2rCB439DarLMWHWYfQNhw6yhWJhcg6bt7ebBYTs3vVeQgSB5kYuifzJF4QQmK6uJyTNPvpV1J6V8M32PBkGT3JpVB3GUGZiksETf7TaF9wAhMo2QAMxw5ZvaQVve5ea7Mane6cfb2Gx69SRff5zDfEQvKqKnyyZje4SGZgWUeHWVLhRjg4KMTJ3JcsHxEqj2k5qeGeyBbgzcuEtCpYvaytsz7nuZGJsT4Z87gB5Zq4NGuDmekuN977eRJvua2dASNWeHiAzVyvnS7ARN5cdUjjYKYiWgHaYrHGsv26WTDeiu4U3sdJMrLHGFY5ihX7f8sTZqD6Wx5AWjQNbEtKaVHymDogfLcwGCC42gQ2yhKfPUaWJ8H4yMB65YBDXGjATaUzcDmJcZKx8g31j2uTVNSFUesd5CRNEEcTNW7cSFFCishCD3T4eV9SuyZyEXAZ48pazPzc1BysBNHEXQNUEtEAZTKmpghC2pihhfDub6LnMJPo9DDdhCULCbcWbGAPc1vPekPaWvk7wrUTGwp5xoNUhQLW3MeJzMvrMSsqLdursCKB4h4Tk272WCStCPQwAKMYoxjWvMzxoUTTWCkhLKHruMtsehRnai4vhu13jbui6ji1F389gfazm4ctth2s4Yw3H3SaPtRETBfZNvZ7n5UV1MD6Q3qin92gT65iqXEi4zRN3woYcK6ZehiSvgUksdEFAUSxNMgNXKtHEYDS6kA37tn5JdBa2Ex2jLudFfhg6JBM226ZKyj65o6feYPgbJAR3jMCmQRHe6DSFb4aH895EowNMjfGUhwhmnbYB1djp7iFXxPP7575NAerhxEQ1WFnxTfoX7pu1Vc9YZb5priCAVbATCaDkECJsdedM45Vx96Jc6E5NWqD98RhMsPimVJkSfYJmRxH9qugica6WonFFb2YLvXYyhoBA1VHBcRqZJ5KHitS5AegYSoYprUfubMzcYo2hGVEQkGKAsFq6jZgCsbJoGLXt3No317vcowB5f3hqT9FjASHAzW2j8uJ9RRzX7XtrPhArwx4EyPgYzrvgG7xcenoSgQt8poa7aYky56eZTKHVUZgUEt6St32MjcivMvmNdWiAHHDc2ZxzTJHgeuCckX7n19vQ3XNLuXv9oGKNNCi8kHnT4tUnnGXNAWXWuyBgZKWUL8u3y41iW6dLYK3Pw5zfpKZTrq3q3bTLJRN5LnnUuFVnWsC3SNqa6VAAvhTGR9PzxLk8C6HeLP2AsYPpqeQwbaL3Ks6tvPdob3tQPWRBGL4uiKtNZ23tRYZGZLYFWZK7psRSZg5AETejKxztVzAuYovpVUiDq71o331tjqWWV1SzWT13Rd1uwz6nHtsjgao2863YaizKARcYr1j9MKtNfDs483yho6i7tbCRR9M4CPLqdiKEaRyVC1FP4F3sejA6nZTuAA35JWUzX6BBj7wgdypMLdMmmtcCZm3bRrF3GvJJs67U8JWRc6dnoGUDaD7rUu ``` -Now lets generate another credit to spend either topping up once the previous one's data allowance has been used, or with another Gateway. Notice that the `ticket-index` is the same: this is generated from the same aggregated credential as the one above! +Now lets generate another ticket to spend either topping up once the previous one's data allowance has been used, or with another Gateway. Notice that the `ticket-index` is the same: this is generated from the same aggregated credential as the one above! ```sh ❯ ./nym-cli ecash generate-ticket --credential-storage storage.db --provider 6qidVK21zpHD298jdDa1RRpbRozP29ENVyqcSbm6hQrG --ticket-index=3 @@ -37,4 +35,4 @@ PAYMENT FOR TICKET 3: Vev3SmwWtH5vbnejX5Zzc1EcxXAgveqHpKNN8arxXaWLhFcEpdcZ6n7qr3NrQUNURWsK2AsUiX8aSiGSjMPEY3iDE3aDYnjYERVow8RKUmQiYSKvz7v9cEJxt97JAHBfu9WYNHXTnLFSJwWuFtBdzY5dzPdzGckFenGCysa1ZBHGADHChDVXKoPHXxpn5qyJxmi48coUQDptR64QgkCeQ8RRZ396Lxw2NKFSjqavCMMDVm3g1rW7cYyPanBhkoAUzPU9KXX1rtmhD6F9gV89mGZ8fm7ByDuKuYU28seLQ7GkVKkhNeRW9XxbjSiyscTnMUzJ24R5VbSdr141BaquUHezdUTzmA2EjAtcyyiVrCMV13cc96CRbMXENP2soUzckFnh1qPnrfKCvX4JYkztq7UgPT2mZEnSTDW4C6Z2NVCNBPNLqUSYrU4id8Jzcp1mBxqJjdYcQ7P5fWJbT5Q9NAq44PCgfXpsUkNoj35QVQvKXKLb5oNGqnua5YC1WBPcENcpS7ZPWpk2hwe8VK4gNgnwQtWH2RPmWbvBREAV97vS1vKNHJyry9sD2PiMJGSmBnb1bKsGxR9UQN3YvRsdGHzyJHzAMTzxbFJBqMPmxjSHJR4UdwzhB81Ludu1RAffTvecWFxmWH5bNymCQjw3wey7Uequcxgyy8KAWYDzvHGwCZQbHQXghsYREiqquZWaa8hX3iTNBFUtEk8PRVT78MoFNdeBWNjsLr8zyZ5EGnf4kqmw3a91g5p5vywf6e3LgMu19VHjPSNtKMNXiatkPEVjsCuCppmV4sB7FsdKKWcMUSWLsdmrDBg9PStHr7NaJRzLL5E91gvysmB36Nob9cHeHSZj3wM4NVVjFfZeRqQf4bi7ahfXjeeBetgDpqx7JcbU6tTN4JpcGUpp7fp4MhTq7MeVQMLweGUVLqewKgAGzCvEmrK6dzLd3U1P9vkAAVZ3cCAKUywnHGxoxDeEfexP1g1EqJLtKNZVKPf7hSMWqGhoQ36K7y5GnyZ5YhQ7jcDME9orm5w4StoxoDdCPcjbakKG7UaTHuhd7tU1mUffXcEvVerkXoQK9SEaKvGks21RBhW86aHUzJWVbkiDzdaqjJWbmzLV8FKvNxNyzucoH2rq8LiHRMZfV1H3SkVSa4j2Ktw7ZGoQfdj8DgekxXSR2nHPfhybzKYXTBqFo2ACisxkjR4rXr9Xo6eYywQhQ1MP6aYgYCAXFGHPoFf7kx7Jns5sWvHRBdaMF65zeFF2m5NDuMWETtLgFfsyNgR84vfSqTfzj2gsUykRei7q9N4LKmiDwBALTAEcTvZpLtXBjc8JaB9PUeBw7DoSiSK376sGrQ9F6ZGTngXACNz1TbvYhtau4bDa6KC2Qn7wmoyrphpn7TtM1jdwGBxLcaEEWZKQHvWVfTyL2itjqnrcAZkxYdCj56oQYwpWfKQk3zJEUA6SYHqyJjaLNVK6u25j7969EWjdpTsJ8qSsZgXi3T7dQqiwintZbUUUKRq7egN1SGVnA6Wup91uKrYUWEWMqVu4g8ipmRsLD9iXHHr3yA21Cka7pqk1FxR9BFTAnkk1 ``` -These are both generated by the _same_ underlying credential and used in a way that they cannot be tied to each other. An ingress Gateway might (for instance) get 100 connection requests from 100 Nym clients, each validated with a zk-nym credi t. It has no way of knowing whether these are all from the same single subscription, or 100 different ones. +These are both generated by the _same_ underlying ticketbook and used in a way that they cannot be tied to each other. An ingress Gateway might (for instance) get 100 connection requests from 100 Nym clients, each validated with a ticket. It has no way of knowing whether these are all from the same single subscription, or 100 different ones. diff --git a/documentation/docs/pages/network/cryptography/zk-nym/zk-nym-overview.mdx b/documentation/docs/pages/network/cryptography/zk-nym/zk-nym-overview.mdx index ec019a3266..14cc7488c6 100644 --- a/documentation/docs/pages/network/cryptography/zk-nym/zk-nym-overview.mdx +++ b/documentation/docs/pages/network/cryptography/zk-nym/zk-nym-overview.mdx @@ -14,7 +14,7 @@ import { Callout } from 'nextra/components' Generation of zk-nyms involves the following actors / pieces of infrastructure: -- **Requester needing a zk-nym** for example a single user using the NymVPN app, or a company purchasing zk-nyms to distribute to their app users, in the instance of an app integrating a Mixnet client via one of the SDKs. The Requester is represented by a Bech32 address on the Nyx blockchain. +- **Requester needing a zk-nym** for example a single user using the NymVPN app, or a company purchasing zk-nyms to distribute to their app users, in the instance of an app integrating a Mixnet client via one of the SDKs. The Requester is represented by a Bech32 address on the Nyx blockchain. - [NymAPI](../../architecture/nyx/nym-api) instances working together on signature generation and spent credential validation, referred to as the **NymAPI Quorum**. Members of the Quorum are a subset of the Nyx chain Validator set (other tasks they perform include a multisig used for triggering reward payouts to the Network Infrastructure Node Operators and maintaining the global Bloom Filter for double-spend protection). - **OrderAPI**: an API creating crypto/fiat to `NYM` swaps and then depositing the NYM tokens in a smart contract managed by the NymAPI Quroum for payment verification. Implementation details of the API will be released in the coming months. @@ -26,8 +26,8 @@ Generation happens in 3 distinct stages: From the perspective of the Requester most of this happens under the hood, but results in the creation and usage of an **unlinkable, rerandomisable anonymous proof-of-payment credential** - a zk-nym - with which to access the Mixnet without fear of doxxing themselves via linking app usage and payment information. The user experience is further enhanced by the fact that a single credential can be split into multiple small zk-nyms, meaning that a Requester may buy a large chunk of bandwidth but 'spend' this in the form of multiple zk-nyms with different ingress Gateways. Whilst this happens under the hood, what it affords the Requester is an ease of experience in that they have to 'top up' their bandwidth less and are able to chop and change ingress points to the Nym Network as they see fit, akin to the UX of most modern day VPNs and dVPNs. ## Key Generation & Payment -- First, a Cosmos [Bech32 address](https://docs.cosmos.network/main/build/spec/addresses/bech32) is created for the Requester. This is used to identify themselves when interacting with the OrderAPI via signed authentication tokens. This is the only identity that the OrderAPI is able to see, and is not able to link this to the zk-nyms that will be generated. This identity never leaves the Requester’s device and there is no email or any personal details needed for signup. If a Requester is simply 'topping up' their subscription, the creation of the address is skipped as it already exists. -- The Requester also generates an ed25519 keypair: this is used to identify and authenticate them in the case of using zk-nyms across several devices as an individual user. However, this is never used in the clear: these keys are used as private attribute values within generated credentials which are verified via zero-knowledge. +- First, a Cosmos [Bech32 address](https://docs.cosmos.network/main/build/spec/addresses/bech32) is created for the Requester. This is used to identify themselves when interacting with the OrderAPI via signed authentication tokens. **This is the only identity that the OrderAPI is able to see, and is not able to link this to the zk-nyms that will be generated.** This identity never leaves the Requester’s device and there is no email or any personal details needed for signup. If a Requester is simply 'topping up' their subscription, the creation of the address is skipped as it already exists. +- The Requester also generates an ed25519 keypair: this is used to identify and authenticate them in the case of using zk-nyms across several devices as an individual user. However, **this is never used in the clear**: these keys are used as private attribute values within generated credentials which are verified via zero-knowledge and not publicly exposed. - The Requester can then interact with various payment backends to pay for their zk-nyms with crypto, fiat options, or natively with NYM tokens. - Payment options will trigger the OrderAPI. This will: - Create a swap for `` to `NYM` tokens. @@ -35,24 +35,24 @@ From the perspective of the Requester most of this happens under the hood, but r - The Requester sends a request to each member of the Quorum requesting a zk-nym credential. This request is signed with their private key and includes the transaction hash of the NYM deposit into the deposit contract, performed either by themselves or the OrderAPI. ## Issue zk-nym -At this point, NYM tokens have been deposited into the smart contract controlled by the Quorum's multisig and a zk-nym credential has been requested. Next, each member of the Quorum who responds to the Requester's request for a zk-nym checks the validity and returns a PSC signed with part of the master key (since this is a threshold cryptsystem, not all members of the Quroum must respond to create a zk-nym, only enough to pass the threshold). The process looks like this: +At this point, NYM tokens have been deposited into the smart contract controlled by the Quorum's multisig and a zk-nym has been requested. Next, each member of the Quorum who responds to the Requester's request for a zk-nym checks the validity and returns a partial blinded signature - a 'partial signed credential' ('PSC') - signed with part of the master key (since this is a threshold cryptsystem, not all members of the Quroum must respond to create a zk-nym, only enough to pass the threshold). The process looks like this: - Members of the Quroum performs several checks to verify the request is valid: - They verify the signature sent as part of the request is valid and that the request was made in the last 48 hours. - They verify that the amount requested matches the amount deposited in the transation, the hash of which was signed by the Requester's ed25519 key and sent as part of the request. -- Members then create a partial blinded signature - a 'partial signed credential' ('PSC') - from their fragment of the master key generated and split amongst them at the beginning of the Quroum in the initial DKG ceremony. +- Members then create a PSC from their fragment of the master key generated and split amongst them at the beginning of the Quroum in the initial DKG ceremony. - The member also creates a `key:value` entry in their local cache with the transaction hash as the key, and the PSC + encrypted signature as the value. This is used later for zk-nym validation and is cleaned after a predefined timeout. - These PSCs are given back to the Requester after setting up a secure channel via DH key exchange, with each replying Quorum member also sending their public key for verification that the returned PSC was signed by them. -Once the Requester has received over the threshold number of PSCs they can assemble them into a credential signed by the master key. The Requester never learns this master key (it is a private attribute) but the credential can be verified by the Quroum as being valid by checking for a proof that the credential's private attribute - the value of the master key - is valid. +Once the Requester has received over the threshold number of PSCs they can assemble them into a 'ticketbook' of 'tickets' - spendable credentials - signed by the master key. The Requester never learns this master key (it is a private attribute) but the credential can be verified by the Quroum as being valid by checking for a proof that the credential's private attribute - the value of the master key - is valid. ![](/images/network/deposit-generate.png) ## Spend zk-nym to Access Mixnet -- Once the credential has been aggregated from the PSCs returned from > threshold of Quorum members, smaller 'zk-nym credits' can be generated from it, accounting for smaller chunks of bandwidth which can be 'spent' with ingress Gateways. This occurs entirely offline, on the device of the zk-nym Requester. See pages on the scheme's [unlinkability](unlinkability.md) and [rerandomisation and incremental spending](./rerandomise.md) features for further information on this. -- This zk-nym credit is later presented to the Quorum by the Gateway that collected it, which is used to calculate reward percentages given to Nym Network infrastructure operators by the Quorum, with payouts triggered by their multisig wallet. Both ingress Gateways and the Quorum use spent zk-nym credits when engaging in [double spending protection](./double-spend-prot.md). +- Once the ticketbook has been aggregated from the PSCs returned from > threshold of Quorum members, smaller 'ticket' credentials can be generated from it, accounting for smaller chunks of bandwidth which can be 'spent' with ingress Gateways. This occurs entirely offline, on the device of the zk-nym Requester. See pages on the scheme's [unlinkability](unlinkability.md) and [rerandomisation and incremental spending](./rerandomise.md) features for further information on this. +- This ticket is later presented to the Quorum by the Gateway that collected it, which is used to calculate reward percentages given to Nym Network infrastructure operators by the Quorum, with payouts triggered by their multisig wallet. Both ingress Gateways and the Quorum use spent tickets when engaging in [double spending protection](./double-spend-prot.md). ![](/images/network/use-zknym.png) diff --git a/documentation/docs/pages/operators/changelog.mdx b/documentation/docs/pages/operators/changelog.mdx index ffcb57e8e0..a649d8de4f 100644 --- a/documentation/docs/pages/operators/changelog.mdx +++ b/documentation/docs/pages/operators/changelog.mdx @@ -25,14 +25,436 @@ export const CiConfig = () => ( ); +export const TunnelManagerCommands = () => ( +
+ Commands to update IP tables rules with a new network_tunnel_manager.sh +
+); + # Changelog -This page displays a full list of all the changes during our release cycle from `v2024.3-eclipse` onwards. Operators can find here the newest updates together with links to relevant documentation. The list is sorted so that the newest changes appear first. +This page displays a full list of all the changes during our release cycle from `v2024.3-eclipse` onward. Operators can find here the newest updates together with links to relevant documentation. The list is sorted so that the newest changes appear first. +## `v2024.14-crunch` + +- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.14-crunch) +- [`nym-node`](nodes/nym-node.mdx) version `1.2.0` +- [Operators updates and tools](changelog#operators-updates--tools) +- [Github CHANGELOG.md](https://github.com/nymtech/nym/blob/nym-binaries-v2024.14-crunch/CHANGELOG.md) + +```sh +nym-node +Binary Name: nym-node +Build Timestamp: 2024-12-11T13:49:11.974104790Z +Build Version: 1.2.0 +Commit SHA: a491e6a71a8cf862d77defd740a4ee8d65d8292a +Commit Date: 2024-12-11T10:28:47.000000000+01:00 +Commit Branch: HEAD +rustc Version: 1.83.0 +rustc Channel: stable +cargo Profile: release +``` + +### Features + +- [Bump elliptic from `6.5.4` to `6.5.7` in /testnet-faucet](https://github.com/nymtech/nym/pull/4768): Bumps [elliptic](https://github.com/indutny/elliptic) from `6.5.4` to `6.5.7`. + +- [build(deps): bump micromatch from `4.0.4` to `4.0.8` in /nym-wallet/webdriver](https://github.com/nymtech/nym/pull/4789): Bumps [micromatch](https://github.com/micromatch/micromatch) from `4.0.4` to `4.0.8`. + +- [build(deps): bump axios from 1.6.0 to 1.7.5 in /nym-api/tests](https://github.com/nymtech/nym/pull/4790) Bumps [axios](https://github.com/axios/axios) from 1.6.0 to 1.7.5. + +- [Sync code with `.env` in build.rs](https://github.com/nymtech/nym/pull/4876): Keep `dotenv` file always up to date + +- [build(deps): bump lazy_static from `1.4.0` to `1.5.0`](https://github.com/nymtech/nym/pull/4913): Bumps [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) from `1.4.0` to `1.5.0`. + +- [Create TaskStatusEvent trait instead of piggybacking on Error](https://github.com/nymtech/nym/pull/4919) + +- [build(deps): bump once_cell from `1.19.0` to `1.20.2`](https://github.com/nymtech/nym/pull/4952): Bumps [`once_cell`](https://github.com/matklad/once_cell) from `1.19.0` to `1.20.2` + +- [Bump the patch-updates group across 1 directory with 10 updates](https://github.com/nymtech/nym/pull/5011): Bumps the patch-updates group with 9 updates in the / directory: + +| Package | From | To | +| --- | --- | --- | +| [anyhow](https://github.com/dtolnay/anyhow) | `1.0.89` | `1.0.90` | +| [clap](https://github.com/clap-rs/clap) | `4.5.18` | `4.5.20` | +| [clap_complete](https://github.com/clap-rs/clap) | `4.5.29` | `4.5.33` | +| [pin-project](https://github.com/taiki-e/pin-project) | `1.1.5` | `1.1.6` | +| [serde](https://github.com/serde-rs/serde) | `1.0.210` | `1.0.211` | +| [serde_json](https://github.com/serde-rs/json) | `1.0.128` | `1.0.132` | +| [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) | `0.2.93` | `0.2.95` | +| [wasm-bindgen-futures](https://github.com/rustwasm/wasm-bindgen) | `0.4.43` | `0.4.45` | +| [web-sys](https://github.com/rustwasm/wasm-bindgen) | `0.3.70` | `0.3.72` | +| Updates `anyhow` | `1.0.89` | `1.0.90` | + +- [[Product Data] Introduce data persistence on gateways](https://github.com/nymtech/nym/pull/5022): This PR builds on top of [\#4974](https://github.com/nymtech/nym/pull/4974), not changing the behavior of the data collection, but persisting them in a sqlite database so they can be kept across restarts and crashes. It also leave the door open for other stats module to use that storage if needed. Here are some points of interest: + - New [`gateway_stats_storage`](https://github.com/nymtech/nym/tree/simon/gateway_stats_persistence/common/gateway-stats-storage) crate + - [Config migration](https://github.com/nymtech/nym/blob/simon/gateway_stats_persistence/nym-node/src/config/old_configs/old_config_v4.rs) resulting from the added database. + - Resulting changes in the [`statistics`](https://github.com/nymtech/nym/tree/simon/gateway_stats_persistence/gateway/src/node/statistics) module to account the new storage system + +- [Integrate nym-credential-proxy into workspace](https://github.com/nymtech/nym/pull/5027): Integrate `nym-credential-proxy` into the main workspace + +- [Authenticator CLI client mode](https://github.com/nymtech/nym/pull/5044) + +- [Node Status API](https://github.com/nymtech/nym/pull/5050): merging a long-diverged feature branch - all commits here were their own merge requests + +- [IPv6 support for wireguard](https://github.com/nymtech/nym/pull/5059) + +- [Add nym node GH workflow](https://github.com/nymtech/nym/pull/5080) + +- [[Product Data] Better unique user count on gateways](https://github.com/nymtech/nym/pull/5084): To avoid double counting clients across gateways, we add a user ID to the gateway session data. + +- [chore: ecash contract migration to remove unused 'redemption_gateway_share'](https://github.com/nymtech/nym/pull/5104) + +- [[Product Data] Client-side stats collection ](https://github.com/nymtech/nym/pull/5107): The goal is to anonymously gather stats from nym-clients. These stats will be sent through the mixnet to a Nym run service provider that will gather them. This PR sets the scene to send stats in a mixnet message to an address. The address can be set when the client is created. Current stats include some infos on sent packets along with platform information. If a receiving address is set, the client will send a mixnet packet every 5min to this address. Otherwise, nothing happens and the client runs as usual. + +- [Send mixnet packet stats using task client](https://github.com/nymtech/nym/pull/5109) + +- [Add granular log on nym-node](https://github.com/nymtech/nym/pull/5111) and make use of it for `defguard_wireguard_rs` big info logs + +- [Rewarding for ticketbook issuance](https://github.com/nymtech/nym/pull/5112): Revamps the current validator rewarder to allow for rewards for issuing the zk-nym ticketbooks. + +- [[Product Data] Add stats reporting configuration in client config ](https://github.com/nymtech/nym/pull/5115): Adds the stats reporting address to client configs. It can be set in the config file, as a CLI argument and as an env var in a `.env` file. As the stats reporting config in now in the `DebugConfig`, the `StatsReportingConfig` is no longer required, making the propagation of these changes more readable + +- [config score](https://github.com/nymtech/nym/pull/5117): introduces a concept of a `config_score` to a nym node which influences performance and thus rewarding amounts and chances of being in the rewarded set. Currently it's influenced by the following factors: + - Accepting terms and conditions (not accepted: 0) + - Exposing self-described API (not exposed: 0) + - Running "nym-node" binary (legacy binary: 0) + - Number of versions behind the core (`score = 0.995 ^ (X * versions_behind ^ 1.65)`) + - The old performance is now treated as `routing_score` + - the "new" performance = `routing_score * config_score` + +- [Add Dockerfile and add env vars for clap arguments](https://github.com/nymtech/nym/pull/5118) + +- [Aadd GH workflow for nym-validator-rewarder](https://github.com/nymtech/nym/pull/5119) + +- [[Product data] Data consumption with ecash ticket](https://github.com/nymtech/nym/pull/5120): Send an event each time an ecash ticket get successfully spent. This allows to approximate how much data each client is using. + +- [[Product Data] Config deserialization bug fix](https://github.com/nymtech/nym/pull/5126): Fixes a bug where a `None` value was serialized into an empty string, and incorrectly deserialized into a `Some` variant. + +- [NS Agent auth with NS API](https://github.com/nymtech/nym/pull/5127): NS Agent authenticates with key that was registered with NS API + - Added flag to Agent to generate keypairs + - Agent requests are signed by agent + - Server-side requests are checked for authentication + +- [CI: reduce jobs running on cluster](https://github.com/nymtech/nym/pull/5132) + +- [Removed ci-nym-api-tests.yml which was running outdated (and broken) tests](https://github.com/nymtech/nym/pull/5133) + +- [[Product Data] Set up country reporting from vpn-client](https://github.com/nymtech/nym/pull/5134): Add the ability to report exit country, along with a small refactoring of a module. + +- [chore: remove standalone legacy mixnode/gateway binaries](https://github.com/nymtech/nym/pull/5135) + +- [Update `serde_json_path` due to compilation issue](https://github.com/nymtech/nym/pull/5144) + +- [Add version to clientStatsReport](https://github.com/nymtech/nym/pull/5147): Add a `kind` and `api_version` field for `ClientStatsReport` + +- [Start session collection for exit gateways](https://github.com/nymtech/nym/pull/5148): Apparently, exit gateways are also entry gateways so we need to start session stats for them as well + +- [build(deps): bump mikefarah/yq from `4.44.3` to `4.44.5`](https://github.com/nymtech/nym/pull/5149): Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from `4.44.3` to `4.44.5`. + +- [build(deps): bump cross-spawn from `7.0.3` to `7.0.6` in /testnet-faucet](https://github.com/nymtech/nym/pull/5150): Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from `7.0.3` to `7.0.6`. + +- [Add export_to_env to NymNetworkDetails](https://github.com/nymtech/nym/pull/5162): In `nym-vpn-core` we've started to read the network environment from a json file and then try to pass around `NymNetworkDetails` directly instead of relying on the exported environment. However we still need to bridge with old code so we need to export the network details instance to the environment. + +- [Add strum::EnumIter for TicketType](https://github.com/nymtech/nym/pull/5164) + +- [Fix env var name](https://github.com/nymtech/nym/pull/5165) + +- [Add support for DELETE to nym-http-api-client](https://github.com/nymtech/nym/pull/5166): Add delete support to `http-api-client` + +- [Add derive_extended_private_key to DirectSecp256k1HdWallet](https://github.com/nymtech/nym/pull/5167): Add `derive_extended_private_key` to `DirectSectp256k1HdWallet` to support seeding ecash keys + +- [Move two minor jobs to free tier github hosted runners](https://github.com/nymtech/nym/pull/5169): In an attempt to easy the load on the self-hosted runners, move two minor workflows over to GH hosted free tier runners. + +- [Remove peers with no allowed ip from storage](https://github.com/nymtech/nym/pull/5175) + +- [Add indexes to monitor run and testing route](https://github.com/nymtech/nym/pull/5181) + +- [Add `monitor_run` and testing_route indexes](https://github.com/nymtech/nym/pull/5182) + +- [`explorer-api`: add nym node endpoints + UI to show nym-nodes and account balances](https://github.com/nymtech/nym/pull/5183): Explorer API: + - Existing endpoints stay identical + - Adds new endpoints to get: + - `nym-nodes` (list + by id) + - account balance + delegations + rewarding + vesting + + - Explorer UI (NextJS) + - List of nym-nodes + - Remove service providers routes (Harbour Master shows these) + - Updates summary page to show nym-nodes + - Adds legacy markers to old gateway and mixnode bond lists + +- [Add `monitor_run` and testing_route indexes](https://github.com/nymtech/nym/pull/5182) + +- [Bugfix/credential proxy sequencing](https://github.com/nymtech/nym/pull/5187) + +- [improvement: make internal gateway clients use the same topology cache](https://github.com/nymtech/nym/pull/5191): This should result in 66% reduction in queries for topology within `nym-node` as all the clients should rely on the same cache + +- [chore: apply 1.84 linter suggestions](https://github.com/nymtech/nym/pull/5192) + +- [Guard storage access with cache](https://github.com/nymtech/nym/pull/5193) + +- [Update Security disclosure email, public key and policy](https://github.com/nymtech/nym/pull/5195) + +- [merge crunch into develop](https://github.com/nymtech/nym/pull/5199) + +- [Fix backwards compat mac generation](https://github.com/nymtech/nym/pull/5202) + +- [adjusted config score penalty calculation](https://github.com/nymtech/nym/pull/5206) + +- [`nym-api` NMv1 adjustments](https://github.com/nymtech/nym/pull/5209) + +- [Nmv2 add debug config](https://github.com/nymtech/nym/pull/5212): Adds debug config to disable poisson process, cover traffic and min performance filtering + +- [introduce UNSTABLE endpoints for returning network monitor run details](https://github.com/nymtech/nym/pull/5214) + +- [Don't consider legacy nodes for rewarded set selection](https://github.com/nymtech/nym/pull/5215) + +- [Derive serialize for UserAgent (#5210)](https://github.com/nymtech/nym/pull/5217): Cherry-pick PR [\#5210](https://github.com/nymtech/nym/pull/5210) + +- [Backport \#5218](https://github.com/nymtech/nym/pull/5220) + +- [Remove any filtering on node semver](https://github.com/nymtech/nym/pull/5224): Removed any filtering on version of nodes. however, the parameters can still be passed to `nym-api` queries to not break existing clients, but they will happily ignore them + +- [Further config score adjustments](https://github.com/nymtech/nym/pull/5225): I still want to add helper endpoints on `nym-api` to expose some of this data. but for now, I'll let this PR bake over the weekend. + +### Bugfix + +- [Correct IPv6 address generation](https://github.com/nymtech/nym/pull/5113) + +- [bugfix: don't send empty BankMsg in ecash contract](https://github.com/nymtech/nym/pull/5121): If ticketbook prices were to be set so low the resultant redemption would have created `BankMsg` with value of 0, that message is no longer going to be sent + +- [fix: validator-rewarder GH job](https://github.com/nymtech/nym/pull/5151) + +- [bugfix: correctly expose ecash-related data on nym-api](https://github.com/nymtech/nym/pull/5155): This PR makes fixes to ecash-related endpoints on `nym-api` + - global data (such as aggregated signatures and keys) are actually always available by all apis + - global data (such as aggregated signatures and keys) are actually always available by all apis + +- [bugfix: use default value for verloc config when deserialising missing values](https://github.com/nymtech/nym/pull/5177) + +- [bugfix: fixed nym-node config migrations (again)](https://github.com/nymtech/nym/pull/5179) + +- [bugfix: added explicit openapi servers to account for route prefixes](https://github.com/nymtech/nym/pull/5237) + +### Operators Updates & Tools + + +**Nym Network will now only allow nodes which [migrated](nodes/nym-node/bonding#migrate-to-nym-node-in-mixnet-smart-contract) their node in Nym mixnet smart contract to Nym Node. All nodes which are still bonded as a legacy one (Mixnode or Gateway) in the wallet will have no chance to take part in the [Rewarded set selection](tokenomics/mixnet-rewards#rewarded-set-selection).** + +**Operators taking part in Delegation program or Service Grant program must migrate their nodes latest by December 16th, 08:00 UTC.** + + +#### Updates + +- [Version count as a part of config score](tokenomics/mixnet-rewards#config-score-calculation) has been introduced. To familiarize yourself with Nym Node operator rewards calculation, read [this page](tokenomics/mixnet-rewards). +- Nym nodes running as Exit Gateway in Service Grant program received delegation. Nym team is now delegating total of **64,800,000 NYM on top 241 Nym Nodes** (137 in Mixnode mode and 104 as Gateways). Our delegation aims to incentivise committed operators who support bootstrapping of Nym network before paying users come. + +
+ + +- 250k NYM - Upgrading to magura in time - 2 nodes +- 300k NYM - Upgrading to magura + bonus for a quick patch upgrade - 102 nodes +- No delegation - not upgrading in time - 2 nodes + + +- `nym-node` has now implemented [IPv6 support for wireguard](https://github.com/nymtech/nym/pull/5059) + +- [`network_tunnel_manager.sh` updated](network): run the commands below to make sure + +
+ +}> +These commands can be run one by one or copy-pasted and run as a block. +```sh +mkdir $HOME/nym-binaries; \ + +curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/network_tunnel_manager.sh -o $HOME/nym-binaries/network_tunnel_manager.sh && \ +chmod +x $HOME/nym-binaries/network_tunnel_manager.sh; \ + +$HOME/nym-binaries/network_tunnel_manager.sh check_nymtun_iptables ; \ +$HOME/nym-binaries/network_tunnel_manager.sh remove_duplicate_rules nymtun0 ;\ +$HOME/nym-binaries/network_tunnel_manager.sh remove_duplicate_rules nymwg;\ +$HOME/nym-binaries/network_tunnel_manager.sh check_nymtun_iptables ; \ +$HOME/nym-binaries/network_tunnel_manager.sh adjust_ip_forwarding ; \ +$HOME/nym-binaries/network_tunnel_manager.sh apply_iptables_rules ; \ +$HOME/nym-binaries/network_tunnel_manager.sh check_nymtun_iptables ; \ +$HOME/nym-binaries/network_tunnel_manager.sh apply_iptables_rules_wg ; \ +$HOME/nym-binaries/network_tunnel_manager.sh configure_dns_and_icmp_wg ; \ +$HOME/nym-binaries/network_tunnel_manager.sh adjust_ip_forwarding ; \ +$HOME/nym-binaries/network_tunnel_manager.sh check_ipv6_ipv4_forwarding; \ + +systemctl daemon-reload && service nym-node restart && journalctl -u nym-node -f +``` + +Then run the jokes in a new window for control +```sh +$HOME/nym-binaries/network_tunnel_manager.sh joke_through_the_mixnet +$HOME/nym-binaries/network_tunnel_manager.sh joke_through_wg_tunnel +``` + + +#### Tools + +- **[New APIs documentation](../apis/introduction)** with interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet. +- [Nym Harbourmaster](https://harbourmaster.nymtech.net/) has a new tab called `CONTRACT EXPLORER` querying data from Nym mixnet contract in real time. +- [Nym Explorer](https://explorer.nymtech.net) is updated to read migrated nodes correctly +- [New community explorer by SpectreDAO](https://explorer.nym.spectredao.net/dashboard) offers Nym Network dashboard, Node overview and Account stats view functions for operators and delegators. +- [`nym-vpnc`](../developers/nymvpncli) build and run documentation, for those who don't want to use the Nym VPN GUIs. + +## `magura-drift` + +Second patch to `v2024.13-magura` release version. + +- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-drift) +- [`nym-node`](nodes/nym-node.mdx) version `1.1.12` + +```sh +nym-node +Binary Name: nym-node +Build Timestamp: 2024-11-29T13:10:51.813092288Z +Build Version: 1.1.12 +Commit SHA: 4a9a5579c40ad956163ea02e01d7b53aef2ac8ef +Commit Date: 2024-11-29T14:06:32.000000000+01:00 +Commit Branch: HEAD +rustc Version: 1.83.0 +rustc Channel: stable +cargo Profile: release +``` + +- This patch adds a peer storage manager to fix issues causing external clients to be blocked, ensuring they can successfully connect to different nodes. + +## `v2024.13-magura-patched` + +- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-patched) +- [`nym-node`](nodes/nym-node.mdx) version `1.1.11` + +```sh +nym-node +Binary Name: nym-node +Build Timestamp: 2024-11-22T14:30:48.067329245Z +Build Version: 1.1.11 +Commit SHA: 01c7b2819ee3d328deccd303b4113ff415d7e276 +Commit Date: 2024-11-22T10:50:59.000000000+01:00 +Commit Branch: HEAD +rustc Version: 1.82.0 +rustc Channel: stable +cargo Profile: release +``` + + + +After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon. + +[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](nodes/performance-and-testing/gateway-probe). + + +### Operators Updates & Tools + +- Updated [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) (moved to our monorepo) helps operators to configure their IP tables rules for `nymtun` and `wireguard` routing. + +- **Please re-run [routing configuration steps](https://nymtech.net/docs/operators/nodes/nym-node/configuration#routing-configuration) to update your routing settings.** + +- We found out that some operators have a wrong value for wireguard IP. Follow these steps to ensure your value is set to `10.1.0.1` (default on new nodes): + + +###### 1. Open your node config file: +```sh +nano $HOME/.nym/nym-nodes//config/config.toml + +# change for your local nym moniker for example: +# nano $HOME/.nym/nym-nodes/default-nym-node/config/config.toml +``` +###### 2. Control or change the value of wireguard private IP +- Scroll down to section starting with `[wireguard]` +- Find line `private_ip` and ensure it's set to value `10.1.0.1` +- The section will look like this: +```toml +[wireguard] +# Specifies whether the wireguard service is enabled on this node. +enabled = true + +# Socket address this node will use for binding its wireguard interface. +# default: `0.0.0.0:51822` +bind_address = '0.0.0.0:51822' + +# Private IP address of the wireguard gateway. +# default: `10.1.0.1` +private_ip = '10.1.0.1' +``` +###### 3. Save, exit and restart the service +- If you used `nano` editor - press `ctrl` + `x` and confirm the changes +- Run these commands to update the service with new values and restart your node process: +```sh +systemctl daemon-reload && service nym-node restart +``` + + +- New manual how to [run `nym-node` as non-root](nodes/nym-node/configuration#running-nym-node-as-a-non-root) + +- Since `v2024.13-magura`, operators do not update their node version in the wallet. [Manual upgrading steps](nodes/maintenance/manual-upgrade.mdx) has been updated accordingly. + +- CLI tool [`node_api_check.py`](nodes/performance-and-testing/node-api-check.mdx), helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it. + +#### Error Log + +In case you encounter this error: +``` +[ERROR] nym-node/src/node/mod.rs:628: the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer.. +``` + +You can follow these steps to make a workaround: + +
+ + + +###### 1. Find the error + +- In the node logs, locate the ERROR message which says `the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer KN5GPvkC+p6G/SM4PD2Z3ObAtRGiDjHPRnQOPpbdUQk=` + +- Copy the end part of that peer, later denoted as `` (in our example `GiDjHPRnQOPpbdUQk=`) to use later in the sql commands + +###### 2. Fix the issue in sqlite3 db + + +Be careful when running commands within sqlite database. + + +- Navigate to the data directory: +```sh +cd $HOME/.nym/nym-nodes//data +``` +- Enter the database: +```sh +sqlite3 clients.sqlite +``` + +- Run these commands: +```sh +# Change with your unique +select * from wireguard_peer where public_key like "%%" +# Make sure that only ONE line is returned and it contains the key + +delete from wireguard_peer where public_key like "%%"; +``` +- Confirm that peer has been removed by running this again: +```sh +select * from wireguard_peer where public_key like "%%"; +``` + +###### 3. Exit and restart the service + +Run `.quit` and: +```sh +systemctl restart nym-node.service +``` + + + ## `v2024.13-magura` Magura release represents a bigger milestone in [project Smoosh](archive/faq/smoosh-faq.mdx) development where `nym-node` is one binary able to perform any function in Nym Mixnet. This release is especially crucial for operators, please pay attention to the section [*Operators Updates & Tooling*](#operators-updates--tooling) below. @@ -70,15 +492,15 @@ cargo Profile: release - [Switch over the last set of jobs to arc runners](https://github.com/nymtech/nym/pull/4938): Switch over the remaining GH jobs using 16-core runners to self-hosted arc runners. Since we can't currently use Docker on the ubuntu-20.04 runners, remove the matrix notification steps -}> +}> Confirm that the deployment workflows work through manual testing - [x] cd-docs - [x] publish-sdk-npm -- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding +- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding -- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived +- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived - [Ticket type storage](https://github.com/nymtech/nym/pull/4947) @@ -103,8 +525,8 @@ Confirm that the deployment workflows work through manual testing - [chore: remove unused rocket code](https://github.com/nymtech/nym/pull/4968) - [add Dockerfile for nym node](https://github.com/nymtech/nym/pull/4972) - -- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type. + +- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type. - [Top up bandwidth](https://github.com/nymtech/nym/pull/4975) @@ -147,7 +569,7 @@ Confirm that the deployment workflows work through manual testing - log gw identity key - better agent testrun logging - log responses on server side - - change response code for agents + - change response code for agents - update sqlx data - fix agent - probe gw bug @@ -165,7 +587,7 @@ Confirm that the deployment workflows work through manual testing - [Allow custom http port to be reset](https://github.com/nymtech/nym/pull/5073) -- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0 +- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0 - [Fix expiration date as today + 7 days](https://github.com/nymtech/nym/pull/5076) @@ -187,7 +609,7 @@ Confirm that the deployment workflows work through manual testing - [Add NYM_VPN_API to env files](https://github.com/nymtech/nym/pull/5099) -- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup +- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup - [`nym-credential-proxy-requests`: reqwest use rustls-tls](https://github.com/nymtech/nym/pull/5116) @@ -205,7 +627,7 @@ Confirm that the deployment workflows work through manual testing - [Fix critical issues SI84 and SI85 from Cure53](https://github.com/nymtech/nym/pull/4758): This pull request fixes the following issues: - NYM-01-009 WP5: BLS12-381 EC signature bypasses in Coconut library (Critical) - NYM-01-014 WP5: Partial signature bypass in offline eCash (Critical) - + - [bugfix: correctly paginate through 'search_tx' endpoint](https://github.com/nymtech/nym/pull/4936): when `results.append(&mut res.txs);` was called, `res.txs` was always empty thus it was impossible to return more than page size number of results - [Fix broken build after merge](https://github.com/nymtech/nym/pull/4937) @@ -214,7 +636,7 @@ Confirm that the deployment workflows work through manual testing - dedicated commands to request specific blocks for processing - decreased websocket failure timeout - ensuring we do actually have sufficient number of blocks to process rewarding for given epoch - - additional error logging + - additional error logging - [bugfix: fix expected return type on /v1/gateways endpoint](https://github.com/nymtech/nym/pull/4965) @@ -224,7 +646,7 @@ Confirm that the deployment workflows work through manual testing - Faulty aggregation to invalid offline eCash signatures - Signature forgery of Pointcheval-Sanders schema -- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed +- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed - [Fix rustfmt in nym-credential-proxy](https://github.com/nymtech/nym/pull/4992) @@ -279,7 +701,7 @@ Confirm that the deployment workflows work through manual testing - [bugfix: make sure to assign correct node_id and identity during 'gateway_details' table migration](https://github.com/nymtech/nym/pull/5142) - [bugifx: assign 'node_id' when converting from 'GatewayDetails' to 'TestNode'](https://github.com/nymtech/nym/pull/5143) - + ### Operators Updates & Tooling @@ -292,7 +714,7 @@ Confirm that the deployment workflows work through manual testing - **[Operators release & rewards roadmap](tokenomics/mixnet-rewards.mdx#roadmap)** -- **New [Operators landing pag e](https://nymtech.net/operators) +- **New [Operators landing page](https://nymtech.net/operators)** - [Nym Harbourmaster](https://harbourmaster.nymtech.net) had a new tab `NODE SEARCH` where operators can easily search nodes by identity keys and owner accounts and get all public information listed. @@ -335,7 +757,7 @@ Confirm that the deployment workflows work through manual testing - Confirm the transaction -###### 5. Welcome to new episode of `nym-node`! +###### 5. Welcome to new episode of `nym-node`! @@ -360,13 +782,13 @@ Confirm that the deployment workflows work through manual testing - Currently in *Native rewarding*, the rewards are split equally across the [rewarded set of nodes](https://validator.nymtech.net/api/v1/epoch/reward_params) (which now = active set and it's size is 240 nodes) for both Mixnet mode and dVPN mode. Every node being assigned 1 / 240 work factor (hence *naive rewarding*). -#### Directory Services v2.1: API & Mixnet Contract Changes +#### Directory Services v2.1: API & Mixnet Contract Changes -Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location. +Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location. Any new bonded node will provide only the bare minimum information: host, identity key and optionally custom port of its HTTP api - we highly recommend to set that one up to `8080`. Everything else will be discovered via the self-described API for maximum flexibility. This also includes the sphinx key, meaning if the API is not exposed, the node will be unable to route any traffic. Furthermore, this allows to arbitrary change of `nym-node` from mixnode into a gateway modes (and vice versa) without losing any delegations. -The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node). +The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node). ##### API High Level Changes diff --git a/documentation/docs/pages/operators/nodes/maintenance/manual-upgrade.mdx b/documentation/docs/pages/operators/nodes/maintenance/manual-upgrade.mdx index 9139ebc7e9..8b3db6b61b 100644 --- a/documentation/docs/pages/operators/nodes/maintenance/manual-upgrade.mdx +++ b/documentation/docs/pages/operators/nodes/maintenance/manual-upgrade.mdx @@ -14,30 +14,27 @@ This page explains how to upgrade [`nym-node`](#nym-node-upgrade) or [`validator ## Nym node Upgrade -**Upgrading your node is a straight forward two-step process:** - - - -#### 1. Updating the binary and `~/.nym/nym-nodes//config/config.toml` on your VPS - -#### 2. Updating the node information in the [mixnet smart contract](https://nymtech.net/docs/nyx/mixnet-contract.html). This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net). - - +Since `v2024.13-magura` (`nym-node v1.1.10`), **operators NO longer update node information in the Mixnet smart contract** (wallet version information), **only upgrade node binary** (on VPS), resulting in `~/.nym/nym-nodes//config/config.toml` update. Below are detailed steps how to do it: -###### 1. Upgrading node binary and information in config file +###### 1. Upgrade `nym-node` binary - Pause your node process. - If you run your node as `systemd` service (recommended), run: `service nym-node stop` - - Otherwise open the terminal window with your node logs and press `ctrl + c` + - Otherwise open the terminal window with your node logs and press once `ctrl + c` and wait for the node to terminate gracefully - Replace the existing `nym-node` binary with the newest binary (which you can either [compile yourself](../../binaries/building-nym.mdx) or [download](../../binaries/pre-built-binaries.mdx). +- To verify node version, run `./nym-node --version` + +###### 2. Restart the node + - [Re-run with the same values](../nym-node/setup.mdx#initialise--run) as you use to run your `nym-node`. If you want keep changes in your config file, use flag `-w` (`--write-changes`), **This will just update the config file, it will not overwrite existing keys**. - - If you automated your node with `systemd` (recommended) run: + +- If you automated your node with `systemd` (recommended), make sure you have all needed flags in `ExecStart` line of the service config file, and run: ```sh systemctl daemon-reload service nym-node start @@ -48,25 +45,19 @@ service nym-node start journalctl -f -u nym-node.service ``` -###### 2. Updating your node information in the smart contract -Follow these steps to update the information about your `nym-node` which is publicly available from the [`nym-api`](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [Mixnet explorer](https://explorer.nymtech.net). +###### 3. Check if your node is reporting the version correctly -You can either do this graphically via the Desktop Wallet, or the CLI. - - -
- Desktop Wallet (recommended), - CLI (superusers), - ]} defaultIndex="0"> - - - -
+- Open [Nym Harbourbourmaster](https://harbourmaster.nymtech.net), search your node and verify that everything is working as expected and your node shows expected version.
+ +After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon. + +[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](../performance-and-testing/gateway-probe). + + ## Validator Upgrade Upgrading from `v0.31.1` -> `v0.32.0` process is fairly simple. Grab the `v0.32.0` release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files: @@ -92,4 +83,3 @@ The most common reason for your validator being jailed is that it runs out of me Running the command `df -H` will return the size of the various partitions of your VPS. If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process. - diff --git a/documentation/docs/pages/operators/nodes/nym-node/bonding.mdx b/documentation/docs/pages/operators/nodes/nym-node/bonding.mdx index 44a46ac714..3c64fe9b29 100644 --- a/documentation/docs/pages/operators/nodes/nym-node/bonding.mdx +++ b/documentation/docs/pages/operators/nodes/nym-node/bonding.mdx @@ -120,7 +120,7 @@ From `nym-wallet` version `1.2.15` onward the application allows and prompts ope ###### 2. Verify the binary and extract it if needed -- Download [`hashes.json`]https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json +- Download [`hashes.json`](https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json) - Open it with your text editor or print it's content with `cat hashes.json` - Run `sha256sum ` for example `sha256sum ./nym-wallet_1.2.15_amd64.AppImage` - If your have to extract it (like `.tar.gz`) do it diff --git a/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx b/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx index ac41036ce6..9a82c0081f 100644 --- a/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx +++ b/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx @@ -301,8 +301,10 @@ chmod +x network_tunnel_manager.sh && \ ###### 3. Setup IP tables rules -- Apply the rules for IPv4 and IPv6: +- Delete IP tables rules for IPv4 and IPv6 and apply new ones: ```sh +./network_tunnel_manager.sh remove_duplicate_rules nymtun0 + ./network_tunnel_manager.sh apply_iptables_rules ``` @@ -363,9 +365,11 @@ operation check_nymtun_iptables completed successfully. ``` -###### 5. Apply rules for wireguad routing +###### 5. Remove old and apply new rules for wireguad routing ```sh +/network_tunnel_manager.sh remove_duplicate_rules nymwg + ./network_tunnel_manager.sh apply_iptables_rules_wg ``` @@ -374,8 +378,15 @@ operation check_nymtun_iptables completed successfully. ```sh ./network_tunnel_manager.sh configure_dns_and_icmp_wg ``` +###### 7. Adjust and validate IP forwarding -###### 7. Check `nymtun0` interface and test routing configuration +```sh +./network_tunnel_manager.sh adjust_ip_forwarding + +./network_tunnel_manager.sh check_ipv6_ipv4_forwarding +``` + +###### 8. Check `nymtun0` interface and test routing configuration ```sh ip addr show nymtun0 @@ -409,7 +420,7 @@ ip addr show nymtun0 - **Note:** WireGuard will return only IPv4 joke, not IPv6. WG IPv6 is under development. Running IPR joke through the mixnet with `./network_tunnel_manager.sh joke_through_the_mixnet` should work with both IPv4 and IPv6! -###### 8. Enable wireguard +###### 9. Enable wireguard Now you can run your node with the `--wireguard-enabled true` flag or add it to your [systemd service config](#systemd). Restart your `nym-node` or [systemd](#2-following-steps-for-nym-nodes-running-as-systemd-service) service (recommended): @@ -426,6 +437,95 @@ journalctl -u nym-node.service -f -n 100 Make sure that you get the validation of all connectivity. If there are still any problems, please refer to [troubleshooting section](../../troubleshooting/vps-isp.mdx#incorrect-gateway-network-check). +## Running `nym-node` as a non-root + +Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. Below is a guide how to go about such setup: + + +Copying nodes database and the `.nym/` directories from `/root/.nym` to `/home//.nym/` should be treated as experimental, therefore we would advise this section for operators starting new nodes, rather than tweaking an existing one. We will publish a detailed guide for changing permissions of an existing node soon. + + + +###### 1. Setup a new user + +- Define a variable `user_name` using your desired user name: +```sh +user_name="" +``` + +- Run: +```sh +user_home="/home/$user_name" + +if ! id "$user_name" &>/dev/null; then + sudo adduser --home "$user_home" --disabled-login --gecos "" "$user_name" +else + echo "user $user_name already exists" +fi +``` + +- And follow by: + +```sh +sudo usermod -aG sudo "$user_name" +``` + +- Optional: Add to sudoers group: +```sh +echo "$user_name ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/$user_name +``` + +###### 2. Grant needed permissions for network-level operations + +While `nym-node` will be set as a user process, it requires higher privileges for network-level operations, set them up with this command: + +```sh +sudo setcap 'cap_net_bind_service=+ep cap_net_admin=+ep' nym-node +``` + +**After replacing or upgrading the binary, you must reapply these permissions each time!** + +###### 3. Edit service config file + +- Add these new lines to your `/etc/systemd/system/nym-node.service` [service config file](#systemd) + - `After=network.target` + - `Group=` + - `Type=simple` + +- Your service file will then look like this: + +```ini +[Unit] +Description=Nym Node +After=network.target +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User= +Group= +Type=simple +LimitNOFILE=65536 +ExecStart=/nym-node run # add all the flags you use to run your node +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +###### 4. Reload and restart the service + +```sh +systemctl daemon-reload && service nym-node restart +``` + +- If you want to follow the logs, run: +```sh +journalctl -u nym-node -f +``` + ## Next Steps diff --git a/documentation/docs/pages/operators/nodes/nym-node/configuration/_meta.json b/documentation/docs/pages/operators/nodes/nym-node/configuration/_meta.json index 3d0ff2819e..a48b50f426 100644 --- a/documentation/docs/pages/operators/nodes/nym-node/configuration/_meta.json +++ b/documentation/docs/pages/operators/nodes/nym-node/configuration/_meta.json @@ -1,3 +1,3 @@ { - "proxy-configuration": "WSS & Reverese Proxy" + "proxy-configuration": "WSS & Reverse Proxy" } diff --git a/documentation/docs/pages/operators/nodes/nym-node/setup.mdx b/documentation/docs/pages/operators/nodes/nym-node/setup.mdx index 8104e58fb3..33e7e3f6c3 100644 --- a/documentation/docs/pages/operators/nodes/nym-node/setup.mdx +++ b/documentation/docs/pages/operators/nodes/nym-node/setup.mdx @@ -17,21 +17,23 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](. ```sh nym-node Binary Name: nym-node -Build Timestamp: 2024-11-18T17:02:50.947941194Z -Build Version: 1.1.10 -Commit SHA: b49ef643df86f0c670672429812c632fbbaf6cf1 -Commit Date: 2024-11-18T17:56:57.000000000+01:00 +Build Timestamp: 2024-12-11T13:49:11.974104790Z +Build Version: 1.2.0 +Commit SHA: a491e6a71a8cf862d77defd740a4ee8d65d8292a +Commit Date: 2024-12-11T10:28:47.000000000+01:00 Commit Branch: HEAD -rustc Version: 1.82.0 +rustc Version: 1.83.0 rustc Channel: stable cargo Profile: release ``` + {/* COMMENTING THIS OUT ASS WE HAVE TO FIGURE OUT HOW TO SHOW THE LATEST VERSION FROM MASTER BRANCH */} ## Summary + To run a new node, you can simply execute the `nym-node` command without any flags. By default, the node will set necessary configurations. If you later decide to change a setting, you can use the `-w` flag. diff --git a/documentation/docs/pages/operators/nodes/performance-and-testing/node-api-check.mdx b/documentation/docs/pages/operators/nodes/performance-and-testing/node-api-check.mdx index 3487984388..43867da088 100644 --- a/documentation/docs/pages/operators/nodes/performance-and-testing/node-api-check.mdx +++ b/documentation/docs/pages/operators/nodes/performance-and-testing/node-api-check.mdx @@ -8,6 +8,10 @@ import NodeApiCheckQueryHelp from 'components/outputs/command-outputs/node-api-c # Node API Check + +CLI tool `node_api_check.py`, helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it. + + Operating a `nym-node` is not a *"set and forget"* endeavor, it takes some work. To diagnose node network performance through querying APIs, is a good knowledge to have. These are the main places to look for API endpoints regarding `nym-node`: @@ -93,9 +97,9 @@ python --version -###### 2. Install `node_api_check.py` and make executable +###### 2. Install `node_api_check.py` and make executable -To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json). +To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json). - If you [compiled from source](../../binaries/building-nym.mdx), you already have both of these files. Note that the latest version of this program may be on `develop` branch. @@ -159,4 +163,3 @@ Another command is `version_count` where at least one `nym-node` version is requ ```sh ./node_api_check version_count 1.1.0 1.1.1 1.1.2 1.1.3 --markdown ``` - diff --git a/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup.mdx b/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup.mdx index 940872c24e..5864d3129d 100644 --- a/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup.mdx +++ b/documentation/docs/pages/operators/nodes/preliminary-steps/vps-setup.mdx @@ -232,6 +232,9 @@ username soft nofile 4096 Then reboot your server and restart your node. +## Running `nym-node` as a non-root + +Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. If you are starting a new `nym-node` and want to run it as a non-root, follow [this guide](../nym-node/configuration#running-nym-node-as-a-non-root) before you proceed with the node setup sections. ## Ports reference tables diff --git a/documentation/docs/pages/operators/tokenomics.mdx b/documentation/docs/pages/operators/tokenomics.mdx index de8e2f66f8..0a65a0051f 100644 --- a/documentation/docs/pages/operators/tokenomics.mdx +++ b/documentation/docs/pages/operators/tokenomics.mdx @@ -161,7 +161,7 @@ In dVPN (2-hop) mode every node which meets the performance criteria, including In both cases, the selection algorithm also looks whether the node runs with [Terms & Conditions](nodes/nym-node/setup.mdx#terms--conditions) accepted **AND** if it's not a legacy binary version. In case either of these criterias are not met, the node will have be excluded from the rewarded set selection. -To read more about rewards calculation, please see next page [*Nym Operators Rewards*](mixnet-rewards.md). +To read more about rewards calculation, please see next page [*Nym Operators Rewards*](tokenomics/mixnet-rewards.md). ## Query Validator API diff --git a/documentation/docs/pages/operators/tokenomics/mixnet-rewards.mdx b/documentation/docs/pages/operators/tokenomics/mixnet-rewards.mdx index 835339627a..b329a227c0 100644 --- a/documentation/docs/pages/operators/tokenomics/mixnet-rewards.mdx +++ b/documentation/docs/pages/operators/tokenomics/mixnet-rewards.mdx @@ -12,6 +12,10 @@ import { Clt } from 'components/callout-custom/CalloutCustom.jsx'; # Nym Operators Rewards + +**Nym Network Rewarded set selection had been upgraded recently. Make sure to read the chapter *[Rewarded Set Selection](#rewarded-set-selection)* below carefully to fully understand all requirements to be rewarded!** + + * Nym tokenomics are based on the research paper [*Reward Sharing for Mixnets*](https://nymtech.net/nym-cryptoecon-paper.pdf) @@ -42,6 +46,7 @@ To make it easier for the reader, we use a highlighting line on the left side, w Nodes bonded with vesting tokens are [not allowed to join rewarded set](https://github.com/nymtech/nym/pull/5129) - read more on [Nym operators forum](https://forum.nymtech.net/t/vesting-accounts-are-no-longer-supported/827). + ## Overview This is a quick summary, to understand the full picture, please see detailed [*Rewards Logic & Calculation*](#rewards-logic--calculation) chapter below. @@ -126,34 +131,98 @@ This is a quick summary, to understand the full picture, please see detailed [*R -### Active Set Selection +### Rewarded Set Selection -*Performance matters!* +For a node to be rewarded, the node must be part of a [Rewarded set](https://validator.nymtech.net/api/v1/epoch/reward_params) (which currently = active set) in the first place. The Rewarded set is freshly selected at the start of each epoch (every 60 min), and it consists of 240 Nym nodes that are probabilistically chosen from all the available nodes. These 240 nodes include 120 gateways and 120 mixnodes (40 for each of 3 mixnet layers). -For a node to be rewarded, the node must be part of a [Rewarded set](https://validator.nymtech.net/api/v1/epoch/reward_params) (which currently = active set) in the first place. The active set is selected in the beginning of each epoch (every 60min) where total of 240 Nym nodes - represented by 120 mixnodes and 120 gateways, are randomly allocated across the layers. +Rewarded set nodes are randomly selected, and their selection chances increase with a node score that includes three parameters: -The algorithm choosing nodes into the active set takes into account node's performance and [stake saturation](../tolkenomics.mdx#stake-saturation), both values being between 0 and 1 and config score which is either 0 or 1. +1. [Config score](#config-score-calculation): highest (`1`) when the node is running the latest version of the software with [T&C's accepted](../nodes/nym-node/setup.mdx#terms--conditions) +2. [Performance](#performance-calculation): highest (`1`) when the node is consistently online and correctly processes all the received traffic +3. [Stake saturation](../tokenomics.mdx#stake-saturation): including bond and delegated stake -**Config score is introduced:** The nodes selection to the active set has a new parameter - `config_score`. Config score currently looks if the node binary is `nym-node` (not legacy `nym-mixnode` or `nym-gateway`) **AND** if [Terms & Conditions](nodes/nym-node/setup.mdx#terms--conditions) are accepted. Config score has binary values of either 0 or 1, with a following logic: +Besides these values, the API is also looking whether the node is bonded in Mixnet smart contract as a Nym Node or legacy node (Mixnode or Gateway). **Only nodes bonded as Nym Node in Mixnet smart contract can be selected to the Rewrded set, if you haven't migrated your node yet, please [follow these steps](../nodes/nym-node/bonding#migrate-to-nym-node-in-mixnet-smart-contract)!** -| **Run `nym-node` binary** | **T&C's accepted** | **`config_score`** | +**The node score is calculated with this formula:** + + +> **active_set_selection_probability = total_stake \* ( config_score \* node_performance ) ^ 20 )** + + +Note that the score helps prioritize some nodes over others. If all available nodes have the same score, then the selection is done uniformly at random. By raising the config and performance components to 20, values of these parameters that are below one incur a heavy penalization for the node’s selection chances. + +Besides these values, the API is also checks whether the node is bonded in Mixnet smart contract as a Nym Node or legacy node (Mixnode or Gateway). **Only nodes bonded as Nym Node in Mixnet smart contract can be selected to the Rewrded set. Thus, if you haven't migrated your node yet, please [follow these steps](../nodes/nym-node/bonding#migrate-to-nym-node-in-mixnet-smart-contract)!** + +#### Config Score Calculation + +The nodes selection to the active set has a new parameter - `config_score`. Config score currently looks into three paramteres: + +1. If the node binary is `nym-node` (not legacy `nym-mixnode` or `nym-gateway`): `1` if `True`, `0` if `False` +2. If [Terms & Conditions](../nodes/nym-node/setup.mdx#terms--conditions) are accepted: `1` if `True`, `0` if `False` +3. Version of `nym-node` binary: decreasing weight for outdated versions, as explained below + +**The `config_score` parameter calculation formula:** + + +> **config_score = is_tc_accepted \* is_nym-node_binary \* ( 0.995 ^ ( ( X * versions_behind) ^ 1.65 ) )** + + +First two points have binary values of either 0 or 1, with a following logic: + +| **Run `nym-node` binary** | **T&C's accepted** | **Value** | | :-- | :-- | ---: | +| True | True | 1 | | True | False | 0 | | False | True | 0 | | False | False | 0 | -| True | True | 1 | +Only if both conditions above are `True` the node can have any chance to be selected, as otherwise the probability will always be 0. -The entire active set selection probablity: +**The `versions_behind` parameter in `config_score` calculation** + +From release `2024.14-crunch` (`nym-node v1.2.0`), the `config_score` parameter takes into account also nodes version. The "current version" is the one marked as `Latest` in our repository. The parameter `versions_behind` indicates the number of versions between the `Latest` version and the version run by the node, and it is factored into the config score with the formula: -> **active_set_selection_probability = config_score \* stake_saturation \* node_performance ^ 20** +> **0.995 ^ ( ( X * versions_behind ) ^ 1.65 )** +> +> where:
+> **X = 1; for patches**
+> **X = 10; for minor versions**
+> **X = 100; for major versions**
-For a comparison we made an example with 5 nodes, where first number is node performance and second stake saturation (assuming all of them `config_score` = 1 and not 0): +> The exact parameters are live accessible on [`/v1/status/config-score-details`](https://validator.nymtech.net/api/swagger/index.html#/Status/config_score_details). + +Our versioning convention is: `major_version . minor_version . patch` + +For example `nym-node` on version `1.2.0` is on 1st major version, 2nd minor and 0 patches. + +Note that the `X` multiplier heavily lowers the `config_score` when nodes are outdated with respect to more significant updates. See the the table and graph below: + +| **Version behind** | **Patches (X = 1)** | **Minor versions (X = 10)** | **Major versions (X = 100)** | +| :-- | --: | --: | --: | +| 0 (current version) | 1.0 | 1.0 | 1.0 | +| 1 | 0.995 | 0.7994 | 0.0000 | +| 2 | 0.9844 | 0.4953 | 0.0000 | +| 3 | 0.9698 | 0.2536 | 0.0000 | +| 4 | 0.9518 | 0.1102 | 0.0000 | +| 5 | 0.9311 | 0.0413 | 0.0000 | + + +![](/images/operators/tokenomics/reward_version_graph.png) + +As you can see above, the algorithm is designed to give maximum selection score (`1`) to the latest version, while non-upgraded nodes receive a lower score. The score decreases faster when the node has failed to make a major version upgrade, and slower when the node is behind only with minor updates. This scoring de-prioritizes the selection of outdated nodes, even if their saturation and performance are high. Nodes are selected probabilistically in each epoch (60 min), according to their scores, to be part of the Rewarded set. This scoring mechanism gives priority to the operators running up-to-date nodes, ensuring that the network is as updated as possible. + +#### Performance Calculation + +Performance is measured by Nym Network Monitor which sends thousands of packages through different routes every 15 minutes and measures how many were dropped on the way. Test result represents percentage of packets succesfully returned (can be anything between 0 and 1). Performance value is nodes average of these tests in last 24h. + +Good performance is much more essential than [total stake](../tokenomics.mdx#stake-saturation), because it's lifted to 20th power in the selection formula. + +For a comparison we made an example with 5 nodes, where first number is node performance and second stake saturation (assuming all of them `config_score` = 1 for simplification):
- + > node_1 = 1.00 ^ 20 \* 1.0 = 1
> node_2 = 1.00 ^ 20 \* 0.5 = 0.5
> node_3 = 0.99 ^ 20 \* 1.0 = 0.818
@@ -161,8 +230,9 @@ For a comparison we made an example with 5 nodes, where first number is node per > node_5 = 0.90 ^ 20 \* 1.0 = 0.122
-As you can see the performance (also known as *Routing score*) is much more important during the active set selection. A node with 100% performance but only 50% stake saturation has much bigger chance to be chosen than a node with 95% performance but full stake saturation and incomparably bigger chance than 90% performing node with 100% stake saturation. +As you can see the performance (also known as *Routing score*) is much more important during the Rewarded set selection. A node with 100% performance but only 50% stake saturation has much bigger chance to be chosen than a node with 95% performance but full stake saturation and incomparably bigger chance than 90% performing node with 100% stake saturation. +The nodes are chosen probababilistically in each epoch (60 min), so even nodes with lower performance will eventually be chosen, just much less often, as their chances decrease. ### Layer Distribution @@ -201,7 +271,7 @@ $33\% - 67\%$ ## Roadmap -We are working on the final architecture of [*Fair Mixnet*](#fair-mixnet) tokenomics implementation. The current design is called [*Naive rewarding*](#naive-rewarding). This is an intermediate step, allowing operators to migrate to `nym-node` in Mixnet smart contract and for the first time recieve delegations and earn rewards for any `nym-node` functionality, in opposite to the past system, where only Mixnodes were able to recieve delegations and rewards. +We are working on the final architecture of [*Fair Mixnet*](#fair-mixnet) tokenomics implementation. The current design is called [*Naive rewarding*](#naive-rewarding). This is an intermediate step, expecting operators to migrate to `nym-node` in Mixnet smart contract and be able to recieve delegations and earn rewards for any `nym-node` functionality, in opposite to the past system, where only Mixnodes were able to recieve delegations and rewards. On November 5th, we presented a release roadmap in live [Operators Townhall](https://www.youtube.com/watch?v=3G1pJqvO2VM) where we explained in detail the steps of Nym node and tokenomics development and the effect it will have on node operators and put it into a rough timeline. diff --git a/documentation/docs/pages/styles.css b/documentation/docs/pages/styles.css index 87255c71af..aa26004976 100644 --- a/documentation/docs/pages/styles.css +++ b/documentation/docs/pages/styles.css @@ -1,12 +1,3 @@ -body { - --colorPrimary: #fc8469; - /* --textPrimary: #121726; */ -} - -div.nextra-code-block > div { - background: var(--colorPrimary) !important; -} - footer { text-align: center; } @@ -20,21 +11,23 @@ footer { /* set width entire div */ .nx-mx-auto.nx-flex.nx-max-w-\[90rem\] { max-width: 110rem; - padding: 0%; + left-padding: 1%; } .nextra-toc { - width: 300px !important; + width: 10px !important; padding-right: 0px; padding-left: 0px; - text-align: right; + /* text-align: right; */ border-left: 1px solid #262626; } -/* Code blocks*/ -.nx-absolute.nx-top-0 { - background: var(--colorPrimary) !important; - color: var(--textPrimary) !important; +.nextra-content { + padding-top: 0 !important; + padding-right: 0 !important; + padding-bottom: 0 !important; + padding-left: 0 !important; + margin: 0 !important; } :is(html[class~="dark"] .dark\:nx-bg-primary-300\/10) { @@ -56,14 +49,6 @@ footer { column-width: 500px; } -/* aside ul > li > a:hover{ - background: #fb6e4ea9 !important; -} */ - -/* aside ul > li > a:active{ - background: #fb6e4e !important; -} */ - /* Links*/ .nx-text-primary-600.nx-underline.nx-decoration-from-font { color: var(--colorPrimary) !important; @@ -73,31 +58,6 @@ footer { color: var(--colorPrimary) !important; } -/* Callouts */ -/* -.nextra-callout { - background-color: #fb6e4e21 !important; -} - -.nx-mt-6 .nx-leading-7 > p { - color: var(--colorPrimary) !important; -} -*/ - -/* Callout Secondary */ -/* -:is(html .dark\:nx-border-yellow-200\/30) { - border-color: transparent !important; -} -*/ - -/* Callout Primary */ -/* -:is(html .dark\:nx-border-blue-200\/30) { - border-color: var(--colorPrimary) !important; -} -*/ - /* Chips*/ .chipContained { background-color: var(--colorPrimary) !important; @@ -138,7 +98,6 @@ a.MuiLink-root { color: var(--colorPrimary) !important; } - .MuiPaper-root.MuiAccordion-root { border-radius: 8px; } diff --git a/documentation/docs/pnpm-lock.yaml b/documentation/docs/pnpm-lock.yaml index 3d10a3f4a9..48f2a523b0 100644 --- a/documentation/docs/pnpm-lock.yaml +++ b/documentation/docs/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@apidevtools/swagger-parser': + specifier: ^10.1.0 + version: 10.1.0(openapi-types@12.1.3) '@coreui/coreui': specifier: ^5.1.2 version: 5.1.2(@popperjs/core@2.11.8) @@ -80,6 +83,9 @@ importers: '@nymproject/sdk-full-fat': specifier: '>=1.2.4-rc.2 || ^1' version: 1.2.3 + '@redocly/cli': + specifier: ^1.25.15 + version: 1.25.15(enzyme@3.11.0) '@types/mdx': specifier: ^2.0.13 version: 2.0.13 @@ -139,6 +145,21 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@apidevtools/json-schema-ref-parser@9.0.6': + resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} + + '@apidevtools/openapi-schemas@2.1.0': + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + + '@apidevtools/swagger-methods@3.0.2': + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + + '@apidevtools/swagger-parser@10.1.0': + resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} + peerDependencies: + openapi-types: '>=7' + '@babel/code-frame@7.25.9': resolution: {integrity: sha512-z88xeGxnzehn2sqZ8UdGQEvYErF1odv2CftxInpSYJt6uHuPe9YjahKZITGs3l5LeI9d2ROG+obuDAoSlqbNfQ==} engines: {node: '>=6.9.0'} @@ -187,6 +208,13 @@ packages: '@braintree/sanitize-url@6.0.4': resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} + '@cfaester/enzyme-adapter-react-18@0.8.0': + resolution: {integrity: sha512-3Z3ThTUouHwz8oIyhTYQljEMNRFtlVyc3VOOHCbxs47U6cnXs8K9ygi/c1tv49s7MBlTXeIcuN+Ttd9aPtILFQ==} + peerDependencies: + enzyme: ^3.11.0 + react: '>=18' + react-dom: '>=18' + '@chain-registry/client@1.53.1': resolution: {integrity: sha512-fNhNzElizS84XnUX8DejLcCPbJE5Fiw/8pVNE+d3hxB+qSXxFR//W+np4WKhCbjxmNt8ON1DVFzbu5HJQ+TJvA==} @@ -335,9 +363,15 @@ packages: '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + '@emotion/is-prop-valid@1.2.2': + resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==} + '@emotion/is-prop-valid@1.3.1': resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} + '@emotion/memoize@0.8.1': + resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} + '@emotion/memoize@0.9.0': resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} @@ -369,6 +403,9 @@ packages: '@emotion/unitless@0.10.0': resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + '@emotion/unitless@0.8.1': + resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + '@emotion/use-insertion-effect-with-fallbacks@1.1.0': resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} peerDependencies: @@ -449,6 +486,9 @@ packages: '@ethersproject/web@5.7.1': resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} + '@exodus/schemasafe@1.3.0': + resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} + '@floating-ui/core@1.6.8': resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} @@ -551,6 +591,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@keplr-wallet/common@0.12.28': resolution: {integrity: sha512-ESQorPZw8PRiUXhsrxED+E1FEWkAdc6Kwi3Az7ce204gMBQDI2j0XJtTd4uCUp+C24Em9fk0samdHzdoB4caIg==} @@ -2356,6 +2399,21 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + '@redocly/ajv@8.11.2': + resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} + + '@redocly/cli@1.25.15': + resolution: {integrity: sha512-ZD743CJX4FpMJvGNE9Cm3gNn8LNRzRjyrYNVPi1C4iIEtrFkr5Zq791qv6gUFehWns71svbVyzWD9ftVTdfqYg==} + engines: {node: '>=14.19.0', npm: '>=7.0.0'} + hasBin: true + + '@redocly/config@0.17.1': + resolution: {integrity: sha512-CEmvaJuG7pm2ylQg53emPmtgm4nW2nxBgwXzbVEHpGas/lGnMyN8Zlkgiz6rPw0unASg6VW3wlz27SOL5XFHYQ==} + + '@redocly/openapi-core@1.25.15': + resolution: {integrity: sha512-/dpr5zpGj2t1Bf7EIXEboRZm1hsJZBQfv3Q1pkivtdAEg3if2khv+b9gY68aquC6cM/2aQY2kMLy8LlY2tn+Og==} + engines: {node: '>=14.19.0', npm: '>=7.0.0'} + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -2529,6 +2587,9 @@ packages: '@types/react@18.3.12': resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} + '@types/stylis@4.2.5': + resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -2713,6 +2774,10 @@ packages: resolution: {integrity: sha512-JlqiAl9CPvTm5kKG0QXmVCWNWoC/XyRMOeT77cQlbxXWllgjf6SqUmaNqFon72C2o5OSZids+5FvLdsw6dvWaw==} hasBin: true + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -2728,6 +2793,18 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -2819,6 +2896,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array.prototype.filter@1.0.4: + resolution: {integrity: sha512-r+mCJ7zXgXElgR4IRC+fkvNCeoaavWBs6EdCso5Tbcf+iEMKzBU/His60lt34WEZ9vlb8wDkZvQGcVI5GwkfoQ==} + engines: {node: '>= 0.4'} + array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -2933,6 +3014,9 @@ packages: bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} @@ -2974,6 +3058,9 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2982,6 +3069,9 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + caniuse-lite@1.0.30001669: resolution: {integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==} @@ -3019,6 +3109,13 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -3033,6 +3130,9 @@ packages: citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -3044,6 +3144,9 @@ packages: resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} engines: {node: '>=18'} + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -3079,6 +3182,9 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -3129,6 +3235,9 @@ packages: peerDependencies: webpack: ^5.1.0 + core-js@3.39.0: + resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==} + cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -3161,6 +3270,16 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -3359,6 +3478,9 @@ packages: supports-color: optional: true + decko@1.2.0: + resolution: {integrity: sha512-m8FnyHXV1QX+S1cl+KPFDIl6NMkxtKsy6+U/aYyjrOqWMuwAwYWu7ePqrsUHtDR5Y8Yk2pi/KIDSgF+vT4cPOQ==} + decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} @@ -3438,6 +3560,9 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -3452,9 +3577,22 @@ packages: dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dompurify@3.1.6: resolution: {integrity: sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==} + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -3486,6 +3624,9 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} + encoding-sniffer@0.2.0: + resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -3497,6 +3638,12 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + enzyme-shallow-equal@1.0.7: + resolution: {integrity: sha512-/um0GFqUXnpM9SvKtje+9Tjoz3f1fpBC3eXRFrNs8kpYn69JljciYP7KZTqM/YQbUY9KUjvKB4jo/q+L6WGGvg==} + + enzyme@3.11.0: + resolution: {integrity: sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -3504,6 +3651,9 @@ packages: resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} engines: {node: '>= 0.4'} + es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -3534,6 +3684,9 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} + es6-promise@3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -3696,6 +3849,13 @@ packages: event-stream@4.0.1: resolution: {integrity: sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -3732,9 +3892,16 @@ packages: resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} engines: {node: '>=6'} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-uri@3.0.3: resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==} + fast-xml-parser@4.5.0: + resolution: {integrity: sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==} + hasBin: true + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -3792,6 +3959,9 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + foreach@2.0.6: + resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==} + foreground-child@3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} @@ -3940,6 +4110,11 @@ packages: h3@1.13.0: resolution: {integrity: sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg==} + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -3970,6 +4145,10 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has@1.0.4: + resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} + engines: {node: '>= 0.4.0'} + hash-base@3.1.0: resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} engines: {node: '>=4'} @@ -4027,13 +4206,26 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + html-element-map@1.3.1: + resolution: {integrity: sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + http-shutdown@1.2.2: resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + http2-client@1.3.5: + resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} + + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -4251,6 +4443,9 @@ packages: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} + is-subset@0.1.1: + resolution: {integrity: sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==} + is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} @@ -4311,6 +4506,10 @@ packages: resolution: {integrity: sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==} hasBin: true + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} @@ -4336,6 +4535,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-pointer@0.6.2: + resolution: {integrity: sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -4450,6 +4652,12 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.escape@4.0.1: + resolution: {integrity: sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==} + + lodash.flattendeep@4.4.0: + resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} + lodash.foreach@4.5.0: resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==} @@ -4501,9 +4709,15 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + map-stream@0.0.7: resolution: {integrity: sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==} + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + markdown-extensions@1.1.1: resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==} engines: {node: '>=0.10.0'} @@ -4511,6 +4725,11 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@4.3.0: + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} + engines: {node: '>= 12'} + hasBin: true + match-sorter@6.3.4: resolution: {integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==} @@ -4743,6 +4962,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -4761,12 +4984,41 @@ packages: mlly@1.7.2: resolution: {integrity: sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==} + mobx-react-lite@4.0.7: + resolution: {integrity: sha512-RjwdseshK9Mg8On5tyJZHtGD+J78ZnCnRaxeQDSiciKVQDUbfZcXhmld0VMxAwvcTnPEHZySGGewm467Fcpreg==} + peerDependencies: + mobx: ^6.9.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + + mobx-react@9.1.1: + resolution: {integrity: sha512-gVV7AdSrAAxqXOJ2bAbGa5TkPqvITSzaPiiEkzpW4rRsMhSec7C2NBCJYILADHKp2tzOAIETGRsIY0UaCV5aEw==} + peerDependencies: + mobx: ^6.9.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + mobx@6.13.5: resolution: {integrity: sha512-/HTWzW2s8J1Gqt+WmUj5Y0mddZk+LInejADc79NJadrWla3rHzmRHki/mnEUH1AvOmbNTZ1BRbKxr8DSgfdjMA==} modern-ahocorasick@1.0.1: resolution: {integrity: sha512-yoe+JbhTClckZ67b2itRtistFKf8yPYelHLc7e5xAwtNAXxM6wJTUx2C7QeVSJFDzKT7bCIFyBVybPMKvmB9AA==} + moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -4791,6 +5043,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -4853,6 +5109,10 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-fetch-h2@2.3.0: + resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==} + engines: {node: 4.x || >=6.0.0} + node-fetch-native@1.6.4: resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} @@ -4869,6 +5129,9 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-readfiles@0.2.0: + resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} + node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} @@ -4891,6 +5154,25 @@ packages: resolution: {integrity: sha512-O/j/ROyX0KGLG7O6Ieut/seQ0oiTpHF2tXAcFbpdTLQFiaNtkyTXXocM1fwpaa60dg1qpWj0nHlbNhx6qwuENQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + oas-kit-common@1.0.8: + resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==} + + oas-linter@3.2.2: + resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==} + + oas-resolver@2.5.6: + resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==} + hasBin: true + + oas-schema-walker@1.1.5: + resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==} + + oas-validator@5.0.8: + resolution: {integrity: sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -4903,6 +5185,10 @@ packages: resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} engines: {node: '>= 0.4'} + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -4943,6 +5229,12 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + openapi-sampler@1.6.0: + resolution: {integrity: sha512-0PKhql1Ms38xSngEztcNQ7EXgssR2jAyVX7RckEln4reynIr/HHwuwM29cDEpiNkk4OkrHoc+7Li9V7WTAPYmw==} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -4985,9 +5277,18 @@ packages: parse-url@8.1.0: resolution: {integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + parse5@7.2.0: resolution: {integrity: sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -5025,6 +5326,12 @@ packages: pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + perfect-scrollbar@1.5.6: + resolution: {integrity: sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==} + + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} @@ -5056,6 +5363,14 @@ packages: pkg-types@1.2.1: resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + polished@4.3.1: + resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} + engines: {node: '>=10'} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -5101,6 +5416,10 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.4.47: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} @@ -5109,6 +5428,10 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + process-warning@1.0.0: resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} @@ -5158,12 +5481,22 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + raf@3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + + railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + rainbow-sprinkles@0.17.3: resolution: {integrity: sha512-tu00bbDozceDz/ajL8YzDn5L0mf0uVHR93v2tfGKKbemKeuPk8R4mXUokleKsA24vzrPiw26iMK53HdRy6HLag==} peerDependencies: '@vanilla-extract/css': ^1 '@vanilla-extract/dynamic': ^2 + randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -5210,6 +5543,11 @@ packages: '@types/react': optional: true + react-shallow-renderer@16.15.0: + resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-stately@3.33.0: resolution: {integrity: sha512-DNPOxYAPuhuXwSuE1s1K7iSgqG2QOBUZq3bsLAd4gUUZje6Qepkhe7TzK2LWarQYAZ3gC9Xhmnz8ie1fdCo0GA==} peerDependencies: @@ -5225,6 +5563,11 @@ packages: '@types/react': optional: true + react-tabs@6.0.2: + resolution: {integrity: sha512-aQXTKolnM28k3KguGDBSAbJvcowOQr23A+CUJdzJtOSDOtTwzEaJA+1U4KwhNL9+Obe+jFS7geuvA7ICQPXOnQ==} + peerDependencies: + react: ^18.0.0 + react-textarea-autosize@8.5.4: resolution: {integrity: sha512-eSSjVtRLcLfFwFcariT77t9hcbVJHQV76b51QjQGarQIHml2+gM2lms0n3XrhnDmgK5B+/Z7TmQk5OHNzqYm/A==} engines: {node: '>=10'} @@ -5265,10 +5608,23 @@ packages: redeyed@2.1.1: resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} + redoc@2.2.0: + resolution: {integrity: sha512-52rz/xJtpUBc3Y/GAkaX03czKhQXTxoU7WnkXNzRLuGwiGb/iEO4OgwcgQqtwHWrYNaZXTyqZ4MAVXpi/e1gAg==} + engines: {node: '>=6.9', npm: '>=3.0.0'} + peerDependencies: + core-js: ^3.1.4 + mobx: ^6.0.4 + react: ^16.8.4 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.4 || ^17.0.0 || ^18.0.0 + styled-components: ^4.1.1 || ^5.1.1 || ^6.0.5 + reflect.getprototypeof@1.0.6: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} + reftools@1.1.9: + resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -5335,6 +5691,10 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -5350,6 +5710,9 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + rst-selector-parser@2.2.3: + resolution: {integrity: sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -5432,6 +5795,9 @@ packages: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -5451,6 +5817,24 @@ packages: shiki@0.14.7: resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} + should-equal@2.0.0: + resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} + + should-format@3.0.3: + resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==} + + should-type-adaptors@1.1.0: + resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==} + + should-type@1.4.0: + resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==} + + should-util@1.0.1: + resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==} + + should@13.2.3: + resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} + side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} @@ -5465,6 +5849,9 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-websocket@9.1.0: + resolution: {integrity: sha512-8MJPnjRN6A8UCp1I+H/dSFyjwJhp6wta4hsVRhjf8w9qBHRzxYt14RaOcjvQnhD1N4yKOddEjflwMnQM4VtXjQ==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -5473,6 +5860,10 @@ packages: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} + slugify@1.4.7: + resolution: {integrity: sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==} + engines: {node: '>=8.0.0'} + sonic-boom@2.8.0: resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} @@ -5522,6 +5913,9 @@ packages: std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + stickyfill@1.1.1: + resolution: {integrity: sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA==} + stream-combiner@0.2.2: resolution: {integrity: sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==} @@ -5600,9 +5994,19 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} + styled-components@6.1.13: + resolution: {integrity: sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==} + engines: {node: '>= 16'} + peerDependencies: + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + styled-jsx@5.1.1: resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -5619,6 +6023,9 @@ packages: stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + stylis@4.3.2: + resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} + stylis@4.3.4: resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==} @@ -5647,6 +6054,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + swagger2openapi@7.0.8: + resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} + hasBin: true + symbol-observable@2.0.3: resolution: {integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==} engines: {node: '>=0.10'} @@ -5767,6 +6178,9 @@ packages: tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.8.0: resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} @@ -5809,6 +6223,11 @@ packages: ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + uint8arrays@3.1.0: resolution: {integrity: sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==} @@ -5818,6 +6237,10 @@ packages: uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici@6.21.0: + resolution: {integrity: sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==} + engines: {node: '>=18.17'} + unenv@1.10.0: resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} @@ -5943,6 +6366,9 @@ packages: uqr@0.1.2: resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} + uri-js-replace@1.0.1: + resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -5952,6 +6378,9 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + url-template@2.0.8: + resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} + use-callback-ref@1.3.2: resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} engines: {node: '>=10'} @@ -6071,9 +6500,17 @@ packages: webpack-cli: optional: true + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -6108,6 +6545,9 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -6141,6 +6581,9 @@ packages: yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -6150,10 +6593,18 @@ packages: engines: {node: '>= 14'} hasBin: true + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@17.0.1: + resolution: {integrity: sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==} + engines: {node: '>=12'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -6187,6 +6638,27 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@apidevtools/json-schema-ref-parser@9.0.6': + dependencies: + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + js-yaml: 3.14.1 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 9.0.6 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + ajv: 8.17.1 + ajv-draft-04: 1.0.0(ajv@8.17.1) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + '@babel/code-frame@7.25.9': dependencies: '@babel/highlight': 7.25.9 @@ -6250,6 +6722,17 @@ snapshots: '@braintree/sanitize-url@6.0.4': {} + '@cfaester/enzyme-adapter-react-18@0.8.0(enzyme@3.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + enzyme: 3.11.0 + enzyme-shallow-equal: 1.0.7 + function.prototype.name: 1.1.6 + has: 1.0.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-shallow-renderer: 16.15.0(react@18.3.1) + '@chain-registry/client@1.53.1': dependencies: '@chain-registry/types': 0.50.1 @@ -6736,10 +7219,16 @@ snapshots: '@emotion/hash@0.9.2': {} + '@emotion/is-prop-valid@1.2.2': + dependencies: + '@emotion/memoize': 0.8.1 + '@emotion/is-prop-valid@1.3.1': dependencies: '@emotion/memoize': 0.9.0 + '@emotion/memoize@0.8.1': {} + '@emotion/memoize@0.9.0': {} '@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1)': @@ -6785,6 +7274,8 @@ snapshots: '@emotion/unitless@0.10.0': {} + '@emotion/unitless@0.8.1': {} + '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1)': dependencies: react: 18.3.1 @@ -6927,6 +7418,8 @@ snapshots: '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 + '@exodus/schemasafe@1.3.0': {} + '@floating-ui/core@1.6.8': dependencies: '@floating-ui/utils': 0.2.8 @@ -7080,6 +7573,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jsdevtools/ono@7.1.3': {} + '@keplr-wallet/common@0.12.28': dependencies: '@keplr-wallet/crypto': 0.12.28 @@ -9999,6 +10494,61 @@ snapshots: '@react-types/shared': 3.25.0(react@18.3.1) react: 18.3.1 + '@redocly/ajv@8.11.2': + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js-replace: 1.0.1 + + '@redocly/cli@1.25.15(enzyme@3.11.0)': + dependencies: + '@redocly/openapi-core': 1.25.15 + abort-controller: 3.0.0 + chokidar: 3.6.0 + colorette: 1.4.0 + core-js: 3.39.0 + form-data: 4.0.1 + get-port-please: 3.1.2 + glob: 7.2.3 + handlebars: 4.7.8 + mobx: 6.13.5 + node-fetch: 2.7.0 + pluralize: 8.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + redoc: 2.2.0(core-js@3.39.0)(enzyme@3.11.0)(mobx@6.13.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + semver: 7.6.3 + simple-websocket: 9.1.0 + styled-components: 6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + yargs: 17.0.1 + transitivePeerDependencies: + - bufferutil + - encoding + - enzyme + - react-native + - supports-color + - utf-8-validate + + '@redocly/config@0.17.1': {} + + '@redocly/openapi-core@1.25.15': + dependencies: + '@redocly/ajv': 8.11.2 + '@redocly/config': 0.17.1 + colorette: 1.4.0 + https-proxy-agent: 7.0.5 + js-levenshtein: 1.1.6 + js-yaml: 4.1.0 + lodash.isequal: 4.5.0 + minimatch: 5.1.6 + node-fetch: 2.7.0 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - encoding + - supports-color + '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.10.4': {} @@ -10200,6 +10750,8 @@ snapshots: '@types/prop-types': 15.7.13 csstype: 3.1.3 + '@types/stylis@4.2.5': {} + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -10616,6 +11168,10 @@ snapshots: fs-extra: 10.1.0 yargs: 17.7.2 + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + acorn-import-attributes@1.9.5(acorn@8.13.0): dependencies: acorn: 8.13.0 @@ -10626,6 +11182,16 @@ snapshots: acorn@8.13.0: {} + agent-base@7.1.1: + dependencies: + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + ajv-draft-04@1.0.0(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -10710,6 +11276,15 @@ snapshots: array-union@2.1.0: {} + array.prototype.filter@1.0.4: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-array-method-boxes-properly: 1.0.0 + es-object-atoms: 1.0.0 + is-string: 1.0.7 + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 @@ -10845,6 +11420,8 @@ snapshots: bn.js@5.2.1: {} + boolbase@1.0.0: {} + bowser@2.11.0: {} brace-expansion@1.1.11: @@ -10898,10 +11475,14 @@ snapshots: get-intrinsic: 1.2.4 set-function-length: 1.2.2 + call-me-maybe@1.0.2: {} + callsites@3.1.0: {} camelcase-css@2.0.1: {} + camelize@1.0.1: {} + caniuse-lite@1.0.30001669: {} cardinal@2.1.1: @@ -10940,6 +11521,29 @@ snapshots: character-reference-invalid@2.0.1: {} + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + + cheerio@1.0.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.1.0 + encoding-sniffer: 0.2.0 + htmlparser2: 9.1.0 + parse5: 7.2.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 6.21.0 + whatwg-mimetype: 4.0.0 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -10963,6 +11567,8 @@ snapshots: dependencies: consola: 3.2.3 + classnames@2.5.1: {} + client-only@0.0.1: {} clipboardy@1.2.2: @@ -10976,6 +11582,12 @@ snapshots: is-wsl: 3.1.0 is64bit: 2.0.0 + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -11010,6 +11622,8 @@ snapshots: color-convert: 2.0.1 color-string: 1.9.1 + colorette@1.4.0: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -11050,6 +11664,8 @@ snapshots: serialize-javascript: 6.0.2 webpack: 5.95.0 + core-js@3.39.0: {} + cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -11105,6 +11721,22 @@ snapshots: crypto-js@4.2.0: {} + css-color-keywords@1.0.0: {} + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-to-react-native@3.2.0: + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + css-what@6.1.0: {} cssesc@3.0.0: {} @@ -11320,6 +11952,8 @@ snapshots: dependencies: ms: 2.1.3 + decko@1.2.0: {} + decode-named-character-reference@1.0.2: dependencies: character-entities: 2.0.2 @@ -11380,6 +12014,8 @@ snapshots: dependencies: path-type: 4.0.0 + discontinuous-range@1.0.0: {} + dlv@1.1.3: {} doctrine@2.1.0: @@ -11395,8 +12031,26 @@ snapshots: '@babel/runtime': 7.25.9 csstype: 3.1.3 + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + dompurify@3.1.6: {} + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + duplexer@0.1.2: {} duplexify@4.1.3: @@ -11438,6 +12092,11 @@ snapshots: emojis-list@3.0.0: {} + encoding-sniffer@0.2.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -11449,6 +12108,36 @@ snapshots: entities@4.5.0: {} + enzyme-shallow-equal@1.0.7: + dependencies: + hasown: 2.0.2 + object-is: 1.1.6 + + enzyme@3.11.0: + dependencies: + array.prototype.flat: 1.3.2 + cheerio: 1.0.0 + enzyme-shallow-equal: 1.0.7 + function.prototype.name: 1.1.6 + has: 1.0.4 + html-element-map: 1.3.1 + is-boolean-object: 1.1.2 + is-callable: 1.2.7 + is-number-object: 1.0.7 + is-regex: 1.1.4 + is-string: 1.0.7 + is-subset: 0.1.1 + lodash.escape: 4.0.1 + lodash.isequal: 4.5.0 + object-inspect: 1.13.2 + object-is: 1.1.6 + object.assign: 4.1.5 + object.entries: 1.1.8 + object.values: 1.2.0 + raf: 3.4.1 + rst-selector-parser: 2.2.3 + string.prototype.trim: 1.2.9 + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -11502,6 +12191,8 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.15 + es-array-method-boxes-properly@1.0.0: {} + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 @@ -11547,6 +12238,8 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 + es6-promise@3.3.1: {} + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -11804,6 +12497,10 @@ snapshots: stream-combiner: 0.2.2 through: 2.3.8 + event-target-shim@5.0.1: {} + + eventemitter3@5.0.1: {} + events@3.3.0: {} execa@0.8.0: @@ -11850,8 +12547,14 @@ snapshots: fast-redact@3.5.0: {} + fast-safe-stringify@2.1.1: {} + fast-uri@3.0.3: {} + fast-xml-parser@4.5.0: + dependencies: + strnum: 1.0.5 + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -11900,6 +12603,8 @@ snapshots: dependencies: is-callable: 1.2.7 + foreach@2.0.6: {} + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 @@ -12079,6 +12784,15 @@ snapshots: uncrypto: 0.1.3 unenv: 1.10.0 + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + has-bigints@1.0.2: {} has-flag@2.0.0: {} @@ -12099,6 +12813,8 @@ snapshots: dependencies: has-symbols: 1.0.3 + has@1.0.4: {} + hash-base@3.1.0: dependencies: inherits: 2.0.4 @@ -12234,10 +12950,31 @@ snapshots: dependencies: react-is: 16.13.1 + html-element-map@1.3.1: + dependencies: + array.prototype.filter: 1.0.4 + call-bind: 1.0.7 + html-void-elements@3.0.0: {} + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + http-shutdown@1.2.2: {} + http2-client@1.3.5: {} + + https-proxy-agent@7.0.5: + dependencies: + agent-base: 7.1.1 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + human-signals@5.0.0: {} iconv-lite@0.6.3: @@ -12419,6 +13156,8 @@ snapshots: dependencies: has-tostringtag: 1.0.2 + is-subset@0.1.1: {} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 @@ -12485,6 +13224,8 @@ snapshots: jiti@2.3.3: {} + js-levenshtein@1.1.6: {} + js-sha3@0.8.0: {} js-tokens@4.0.0: {} @@ -12504,6 +13245,10 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-pointer@0.6.2: + dependencies: + foreach: 2.0.6 + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -12619,6 +13364,10 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.escape@4.0.1: {} + + lodash.flattendeep@4.4.0: {} + lodash.foreach@4.5.0: {} lodash.get@4.4.2: {} @@ -12658,12 +13407,18 @@ snapshots: dependencies: react: 18.3.1 + lunr@2.3.9: {} + map-stream@0.0.7: {} + mark.js@8.11.1: {} + markdown-extensions@1.1.1: {} markdown-table@3.0.4: {} + marked@4.3.0: {} + match-sorter@6.3.4: dependencies: '@babel/runtime': 7.25.9 @@ -13194,6 +13949,10 @@ snapshots: dependencies: brace-expansion: 1.1.11 + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + minimatch@9.0.3: dependencies: brace-expansion: 2.0.1 @@ -13213,10 +13972,28 @@ snapshots: pkg-types: 1.2.1 ufo: 1.5.4 + mobx-react-lite@4.0.7(mobx@6.13.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + mobx: 6.13.5 + react: 18.3.1 + use-sync-external-store: 1.2.2(react@18.3.1) + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + mobx-react@9.1.1(mobx@6.13.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + mobx: 6.13.5 + mobx-react-lite: 4.0.7(mobx@6.13.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + mobx@6.13.5: {} modern-ahocorasick@1.0.1: {} + moo@0.5.2: {} + mri@1.2.0: {} ms@2.1.3: {} @@ -13235,6 +14012,13 @@ snapshots: natural-compare@1.4.0: {} + nearley@2.20.1: + dependencies: + commander: 2.20.3 + moo: 0.5.2 + railroad-diagrams: 1.0.0 + randexp: 0.4.6 + neo-async@2.6.2: {} next-mdx-remote@4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -13349,6 +14133,10 @@ snapshots: node-addon-api@7.1.1: {} + node-fetch-h2@2.3.0: + dependencies: + http2-client: 1.3.5 + node-fetch-native@1.6.4: {} node-fetch@2.7.0: @@ -13357,6 +14145,10 @@ snapshots: node-forge@1.3.1: {} + node-readfiles@0.2.0: + dependencies: + es6-promise: 3.3.1 + node-releases@2.0.18: {} non-layered-tidy-tree-layout@2.0.2: {} @@ -13373,12 +14165,52 @@ snapshots: npm-to-yarn@2.2.1: {} + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + oas-kit-common@1.0.8: + dependencies: + fast-safe-stringify: 2.1.1 + + oas-linter@3.2.2: + dependencies: + '@exodus/schemasafe': 1.3.0 + should: 13.2.3 + yaml: 1.10.2 + + oas-resolver@2.5.6: + dependencies: + node-fetch-h2: 2.3.0 + oas-kit-common: 1.0.8 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 17.7.2 + + oas-schema-walker@1.1.5: {} + + oas-validator@5.0.8: + dependencies: + call-me-maybe: 1.0.2 + oas-kit-common: 1.0.8 + oas-linter: 3.2.2 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + reftools: 1.1.9 + should: 13.2.3 + yaml: 1.10.2 + object-assign@4.1.1: {} object-hash@3.0.0: {} object-inspect@1.13.2: {} + object-is@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + object-keys@1.1.1: {} object.assign@4.1.5: @@ -13431,6 +14263,14 @@ snapshots: dependencies: mimic-fn: 4.0.0 + openapi-sampler@1.6.0: + dependencies: + '@types/json-schema': 7.0.15 + fast-xml-parser: 4.5.0 + json-pointer: 0.6.2 + + openapi-types@12.1.3: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -13486,10 +14326,21 @@ snapshots: dependencies: parse-path: 7.0.0 + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.2.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.2.0 + parse5@7.2.0: dependencies: entities: 4.5.0 + path-browserify@1.0.1: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -13515,6 +14366,10 @@ snapshots: dependencies: through: 2.3.8 + perfect-scrollbar@1.5.6: {} + + performance-now@2.1.0: {} + periscopic@3.1.0: dependencies: '@types/estree': 1.0.6 @@ -13556,6 +14411,12 @@ snapshots: mlly: 1.7.2 pathe: 1.1.2 + pluralize@8.0.0: {} + + polished@4.3.1: + dependencies: + '@babel/runtime': 7.25.9 + possible-typed-array-names@1.0.0: {} postcss-import@15.1.0(postcss@8.4.47): @@ -13595,6 +14456,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.4.38: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.4.47: dependencies: nanoid: 3.3.7 @@ -13603,6 +14470,8 @@ snapshots: prelude-ls@1.2.1: {} + prismjs@1.29.0: {} + process-warning@1.0.0: {} prop-types@15.8.1: @@ -13656,11 +14525,22 @@ snapshots: radix3@1.1.2: {} + raf@3.4.1: + dependencies: + performance-now: 2.1.0 + + railroad-diagrams@1.0.0: {} + rainbow-sprinkles@0.17.3(@vanilla-extract/css@1.16.0(babel-plugin-macros@3.1.0))(@vanilla-extract/dynamic@2.1.2): dependencies: '@vanilla-extract/css': 1.16.0(babel-plugin-macros@3.1.0) '@vanilla-extract/dynamic': 2.1.2 + randexp@0.4.6: + dependencies: + discontinuous-range: 1.0.0 + ret: 0.1.15 + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -13743,6 +14623,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 + react-shallow-renderer@16.15.0(react@18.3.1): + dependencies: + object-assign: 4.1.1 + react: 18.3.1 + react-is: 18.3.1 + react-stately@3.33.0(react@18.3.1): dependencies: '@react-stately/calendar': 3.5.5(react@18.3.1) @@ -13780,6 +14666,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 + react-tabs@6.0.2(react@18.3.1): + dependencies: + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-textarea-autosize@8.5.4(@types/react@18.3.12)(react@18.3.1): dependencies: '@babel/runtime': 7.25.9 @@ -13826,6 +14718,41 @@ snapshots: dependencies: esprima: 4.0.1 + redoc@2.2.0(core-js@3.39.0)(enzyme@3.11.0)(mobx@6.13.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + dependencies: + '@cfaester/enzyme-adapter-react-18': 0.8.0(enzyme@3.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@redocly/openapi-core': 1.25.15 + classnames: 2.5.1 + core-js: 3.39.0 + decko: 1.2.0 + dompurify: 3.1.6 + eventemitter3: 5.0.1 + json-pointer: 0.6.2 + lunr: 2.3.9 + mark.js: 8.11.1 + marked: 4.3.0 + mobx: 6.13.5 + mobx-react: 9.1.1(mobx@6.13.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + openapi-sampler: 1.6.0 + path-browserify: 1.0.1 + perfect-scrollbar: 1.5.6 + polished: 4.3.1 + prismjs: 1.29.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-tabs: 6.0.2(react@18.3.1) + slugify: 1.4.7 + stickyfill: 1.1.1 + styled-components: 6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + swagger2openapi: 7.0.8 + url-template: 2.0.8 + transitivePeerDependencies: + - encoding + - enzyme + - react-native + - supports-color + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 @@ -13836,6 +14763,8 @@ snapshots: globalthis: 1.0.4 which-builtin-type: 1.1.4 + reftools@1.1.9: {} + regenerator-runtime@0.14.1: {} regexp.prototype.flags@1.5.3: @@ -13937,6 +14866,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + ret@0.1.15: {} + reusify@1.0.4: {} rimraf@3.0.2: @@ -13950,6 +14881,11 @@ snapshots: robust-predicates@3.0.2: {} + rst-selector-parser@2.2.3: + dependencies: + lodash.flattendeep: 4.4.0 + nearley: 2.20.1 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -14051,6 +14987,8 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + shallowequal@1.1.0: {} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -14070,6 +15008,32 @@ snapshots: vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 + should-equal@2.0.0: + dependencies: + should-type: 1.4.0 + + should-format@3.0.3: + dependencies: + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + + should-type-adaptors@1.1.0: + dependencies: + should-type: 1.4.0 + should-util: 1.0.1 + + should-type@1.4.0: {} + + should-util@1.0.1: {} + + should@13.2.3: + dependencies: + should-equal: 2.0.0 + should-format: 3.0.3 + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + should-util: 1.0.1 + side-channel@1.0.6: dependencies: call-bind: 1.0.7 @@ -14085,10 +15049,24 @@ snapshots: dependencies: is-arrayish: 0.3.2 + simple-websocket@9.1.0: + dependencies: + debug: 4.3.7 + queue-microtask: 1.2.3 + randombytes: 2.1.0 + readable-stream: 3.6.2 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + slash@3.0.0: {} slash@4.0.0: {} + slugify@1.4.7: {} + sonic-boom@2.8.0: dependencies: atomic-sleep: 1.0.0 @@ -14142,6 +15120,8 @@ snapshots: std-env@3.7.0: {} + stickyfill@1.1.1: {} + stream-combiner@0.2.2: dependencies: duplexer: 0.1.2 @@ -14237,10 +15217,26 @@ snapshots: strip-json-comments@3.1.1: {} + strnum@1.0.5: {} + style-to-object@0.4.4: dependencies: inline-style-parser: 0.1.1 + styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@emotion/is-prop-valid': 1.2.2 + '@emotion/unitless': 0.8.1 + '@types/stylis': 4.2.5 + css-to-react-native: 3.2.0 + csstype: 3.1.3 + postcss: 8.4.38 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + shallowequal: 1.1.0 + stylis: 4.3.2 + tslib: 2.6.2 + styled-jsx@5.1.1(babel-plugin-macros@3.1.0)(react@18.3.1): dependencies: client-only: 0.0.1 @@ -14250,6 +15246,8 @@ snapshots: stylis@4.2.0: {} + stylis@4.3.2: {} + stylis@4.3.4: {} sucrase@3.35.0: @@ -14280,6 +15278,22 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + swagger2openapi@7.0.8: + dependencies: + call-me-maybe: 1.0.2 + node-fetch: 2.7.0 + node-fetch-h2: 2.3.0 + node-readfiles: 0.2.0 + oas-kit-common: 1.0.8 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + oas-validator: 5.0.8 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + symbol-observable@2.0.3: {} system-architecture@0.1.0: {} @@ -14409,6 +15423,8 @@ snapshots: tslib@1.14.1: {} + tslib@2.6.2: {} + tslib@2.8.0: {} type-check@0.4.0: @@ -14457,6 +15473,9 @@ snapshots: ufo@1.5.4: {} + uglify-js@3.19.3: + optional: true + uint8arrays@3.1.0: dependencies: multiformats: 9.9.0 @@ -14470,6 +15489,8 @@ snapshots: uncrypto@0.1.3: {} + undici@6.21.0: {} + unenv@1.10.0: dependencies: consola: 3.2.3 @@ -14605,6 +15626,8 @@ snapshots: uqr@0.1.2: {} + uri-js-replace@1.0.1: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -14616,6 +15639,8 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + url-template@2.0.8: {} + use-callback-ref@1.3.2(@types/react@18.3.12)(react@18.3.1): dependencies: react: 18.3.1 @@ -14750,8 +15775,14 @@ snapshots: - esbuild - uglify-js + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-fetch@3.6.20: {} + whatwg-mimetype@4.0.0: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -14809,6 +15840,8 @@ snapshots: word-wrap@1.2.5: {} + wordwrap@1.0.0: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -14834,12 +15867,26 @@ snapshots: yallist@2.1.2: {} + yaml-ast-parser@0.0.43: {} + yaml@1.10.2: {} yaml@2.6.0: {} + yargs-parser@20.2.9: {} + yargs-parser@21.1.1: {} + yargs@17.0.1: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + yargs@17.7.2: dependencies: cliui: 8.0.1 diff --git a/documentation/docs/public/favicon.ico b/documentation/docs/public/favicon.ico new file mode 100644 index 0000000000..c0e5719e27 Binary files /dev/null and b/documentation/docs/public/favicon.ico differ diff --git a/documentation/docs/public/images/operators/tokenomics/reward_version_graph.png b/documentation/docs/public/images/operators/tokenomics/reward_version_graph.png new file mode 100644 index 0000000000..5794874270 Binary files /dev/null and b/documentation/docs/public/images/operators/tokenomics/reward_version_graph.png differ diff --git a/documentation/docs/theme.config.tsx b/documentation/docs/theme.config.tsx index 3acf475992..1ad62048a3 100644 --- a/documentation/docs/theme.config.tsx +++ b/documentation/docs/theme.config.tsx @@ -1,14 +1,59 @@ import React from "react"; -import { DocsThemeConfig } from "nextra-theme-docs"; +import { DocsThemeConfig, useConfig } from "nextra-theme-docs"; import { Footer } from "./components/footer"; import { Matrix } from "./components/matrix-link"; import { useRouter } from "next/router"; const config: DocsThemeConfig = { - useNextSeoProps() { - return { - titleTemplate: "%s – Nym Docs", - }; + head: function useHead() { + const config = useConfig() + const { route } = useRouter() + const url = process.env.NEXT_PUBLIC_SITE_URL + const image = url + '/nym_logo.jpg' + + // Define descriptions for different "books" + const bookDescriptions: Record = { + '/developers': "Nym's developer documentation covering core concepts of integrating with the Mixnet, interacting with the Nyx blockchain, an overview of the avaliable tools, and our SDK docs.", + '/network': "Nym's network documentation covering network architecture, node types, tokenomics, and cryptography.", + '/operators': "Nym's Operators guide containing information and setup guides for the various components of Nym network and Nyx blockchain validators.", + '/apis': "Interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet." + } + + const defaultDescription = 'Nym is a privacy platform. It provides strong network-level privacy against sophisticated end-to-end attackers, and anonymous access control using blinded, re-randomizable, decentralized credentials.' + + const topLevel = '/' + route.split('/')[1] + const description = + config.frontMatter.description || + bookDescriptions[topLevel] || + defaultDescription + + const title = config.title + (route === '/' ? '' : ' - Nym docs') + + return ( + <> + {title} + + + + + + + + + + + + + + + + + + + + + + ) }, logo: Nym Docs, project: { @@ -28,16 +73,16 @@ const config: DocsThemeConfig = { extraContent: , }, toc: { - float: true, // TODO would be nice to set this to false so the TOC is in the left sidebar but this doesn't seem to work with pages that are also the top of directories: fix + float: false, // TODO would be nice to set this to false so the TOC is in the left sidebar but this doesn't seem to work with pages that are also the top of directories: fix // if we do this then we also have to uncomment the editLink and feedback objects below + component: null, // try remove right-hand column + }, + editLink: { + component: null, // remove element + }, + feedback: { + content: null, // remove element }, - // editLink: { - // component: null, - // }, - // feedback: { - // content: null, - // }, - // gitTimestamp: TODO }; diff --git a/documentation/scripts/next-scripts/autodoc.sh b/documentation/scripts/next-scripts/autodoc.sh index b04fff82cf..bbc2bfda93 100755 --- a/documentation/scripts/next-scripts/autodoc.sh +++ b/documentation/scripts/next-scripts/autodoc.sh @@ -6,9 +6,9 @@ set -o errexit set -o nounset set -o pipefail -# make sure we have all the binaries built -cd ../../ && cargo build --release && cd tools/nym-cli && cargo build --release && cd ../../ && - +# make sure we have all the binaries built from master +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) && git checkout master && +cd ../../ && cargo build --release && cd tools/nym-cli && cargo build --release && cd ../../ && git checkout $CURRENT_BRANCH && # run autodoc script cd documentation/autodoc/ && cargo run --release && diff --git a/documentation/scripts/rewards_version_graph.py b/documentation/scripts/rewards_version_graph.py new file mode 100644 index 0000000000..6177c3d7e2 --- /dev/null +++ b/documentation/scripts/rewards_version_graph.py @@ -0,0 +1,67 @@ +import matplotlib.pyplot as plt +import matplotlib.axes as ax +import matplotlib.pylab as pylab +from matplotlib.pyplot import figure +import numpy as np + +plt.style.use('dark_background') + +a = 0.995 +b = 1.65 + +# make data +x1 = [0,1,2,3,4,5] +x2 = x1 +x3 = x1 +x4 = x1 + +y1 = [a**((v*1)**b) for v in x1] +y2 = [a**((v*10)**b) for v in x1] +y3 = [a**((v*100)**b) for v in x1] +# y4 = [a**((11)**b) for v in x1] + +f = plt.figure() +f.set_figwidth(12) +f.set_figheight(9) + +# plot +#fig, ax = plt.subplots() +plt.plot(x1,y1, label=f'Patches behind: config_score_multiplier = {a} ^ ((1 * versions_behind) ^ {b})') +plt.plot(x2,y2, label=f'Minor versions behind: config_score_multiplier = {a} ^ ((10 * versions_behind) ^ {b})') +plt.plot(x3,y3, label=f'Major versions behind: config_score_multiplier = {a} ^ ((100 * versions_behind) ^ {b})') +#ax.plot(x, y, linewidth=2.0) + + +# naming the x axis +plt.xlabel('Nym Node versions behind the current one', fontsize=20) + + +# naming the y axis +plt.ylabel('Config score multiplier', fontsize=20) + +# giving a title to my graph +plt.title('Nym node version config score multiplier', fontsize=28) + + +#ax.Axes.set_xticks([x]) +#ax.Axes.set_yticks([y]) + +plt.legend(fontsize=12) + +#params = {'legend.fontsize': 20, +# 'axes.labelsize': 24, +# 'axes.titlesize':'x-large', +# 'xtick.labelsize':20, +# 'ytick.labelsize':20} +# +#pylab.rcParams.update(params) + +# set the limits +plt.xlim([0, 5]) +plt.ylim([0,1]) + + + +#plt.show() + +plt.savefig('../docs/public/images/operators/tokenomics/reward_version_graph.png') diff --git a/envs/qa.env b/envs/qa.env index 81adaf299c..0a53e1aa0c 100644 --- a/envs/qa.env +++ b/envs/qa.env @@ -20,5 +20,6 @@ REWARDING_VALIDATOR_ADDRESS=n1rfvpsynktze6wvn6ldskj8xgwfzzk5v6pnff39 EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api/ NYXD=https://qa-validator.qa.nymte.ch +NYXD_WS=wss://qa-validator.qa.nymte.ch/websocket/ NYM_API=https://qa-nym-api.qa.nymte.ch/api/ NYM_VPN_API=https://nym-vpn-api-git-deploy-qa-nyx-network-staging.vercel.app/api/ diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index 6fe9830dae..fd2880c4fd 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "explorer-api" -version = "1.1.43" +version = "1.1.44" edition = "2021" license.workspace = true diff --git a/explorer-api/src/gateways/models.rs b/explorer-api/src/gateways/models.rs index d2298b2a0c..9b8a6dfaef 100644 --- a/explorer-api/src/gateways/models.rs +++ b/explorer-api/src/gateways/models.rs @@ -120,9 +120,7 @@ impl ThreadsafeGatewayCache { .read() .await .get(&identity_key) - .map_or(false, |cache_item| { - cache_item.valid_until > SystemTime::now() - }) + .is_some_and(|cache_item| cache_item.valid_until > SystemTime::now()) } pub(crate) async fn get_locations(&self) -> GatewayLocationCache { diff --git a/explorer-api/src/mix_nodes/models.rs b/explorer-api/src/mix_nodes/models.rs index ce5876c949..a9e85ab97e 100644 --- a/explorer-api/src/mix_nodes/models.rs +++ b/explorer-api/src/mix_nodes/models.rs @@ -106,9 +106,7 @@ impl ThreadsafeMixNodesCache { .read() .await .get(&mix_id) - .map_or(false, |cache_item| { - cache_item.valid_until > SystemTime::now() - }) + .is_some_and(|cache_item| cache_item.valid_until > SystemTime::now()) } pub(crate) async fn get_locations(&self) -> MixnodeLocationCache { diff --git a/explorer-api/src/unstable/models.rs b/explorer-api/src/unstable/models.rs index 39254d9c07..9abb81942d 100644 --- a/explorer-api/src/unstable/models.rs +++ b/explorer-api/src/unstable/models.rs @@ -60,9 +60,7 @@ impl ThreadSafeNymNodesCache { .read() .await .get(&node_id) - .map_or(false, |cache_item| { - cache_item.valid_until > SystemTime::now() - }) + .is_some_and(|cache_item| cache_item.valid_until > SystemTime::now()) } pub(crate) async fn get_bonded_nymnodes( diff --git a/explorer-nextjs/app/components/Nav/DesktopNav.tsx b/explorer-nextjs/app/components/Nav/DesktopNav.tsx index 618037ea04..e1faab46f1 100644 --- a/explorer-nextjs/app/components/Nav/DesktopNav.tsx +++ b/explorer-nextjs/app/components/Nav/DesktopNav.tsx @@ -20,6 +20,7 @@ import { NYM_WEBSITE } from '@/app/api/constants' import { useMainContext } from '@/app/context/main' import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose' import { NavOptionType, originalNavOptions } from '@/app/context/nav' +import { ReleaseAlert } from '@/app/components/ReleaseAlert' import { DarkLightSwitchDesktop } from '@/app/components/Switch' import { Footer } from '@/app/components/Footer' import { ConnectKeplrWallet } from '@/app/components/Wallet/ConnectKeplrWallet' @@ -369,6 +370,7 @@ export const Nav: FCWithChildren = ({ children }) => { style={{ width: `calc(100% - ${drawerWidth}px` }} sx={{ py: 5, px: 6, mt: 7 }} > + {children}