Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f9ac1286b | |||
| 961775417f | |||
| 044d94ad02 | |||
| 35d7525d1e | |||
| b393405db8 | |||
| 6974f3d785 | |||
| 85d96deded | |||
| 4fe7ce0f12 | |||
| 5886361dc5 | |||
| 40c26b1326 | |||
| 46a482cfcb | |||
| 761b6c2cac | |||
| 0afdd7bc82 | |||
| 7b9fe3dc09 | |||
| d734174f6e | |||
| 6187d94b68 | |||
| a7dca2f07c | |||
| 2ed4449e0b | |||
| 605176551b | |||
| ac3830e677 | |||
| c00e4655f4 | |||
| 8fa84a28e6 | |||
| 94d3bf087a | |||
| 7a9b989db9 | |||
| 9f1b89616a | |||
| 89315f0c2a | |||
| 858fafb1a5 | |||
| 169f8f2c1c | |||
| 8782fd7bb8 | |||
| 146c3bd358 | |||
| e68ebdc2b8 | |||
| 397b03267a | |||
| 2a021b46ac | |||
| c43cb657c6 | |||
| 6f66b377e2 | |||
| 5ea67a9376 | |||
| f566dffc5b | |||
| 05f8beedad | |||
| 2fff051e28 | |||
| 44bd70c546 | |||
| 702354d127 | |||
| 56b1010d16 | |||
| 0632517f5d | |||
| bfcc5e9b41 | |||
| 337aacd442 | |||
| da5b7302b5 | |||
| 7a53e86b40 | |||
| 654dd07d19 | |||
| ef0765face | |||
| 3685b4681c | |||
| 47e2af2caa | |||
| 5be555d79f | |||
| 8cc2b3167e | |||
| 4d95955961 | |||
| e36ae4091f | |||
| b566147f2f | |||
| 12242bb3c6 | |||
| 0a0b0e80f4 |
@@ -0,0 +1,61 @@
|
||||
name: build-upload-binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
add_tokio_unstable:
|
||||
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
NETWORK: mainnet
|
||||
|
||||
jobs:
|
||||
publish-nym:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
continue-on-error: true
|
||||
|
||||
- name: Sets env vars for tokio if set in manual dispatch inputs
|
||||
run: |
|
||||
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-binaries-artifacts
|
||||
path: |
|
||||
target/release/nym-client
|
||||
target/release/nym-gateway
|
||||
target/release/nym-mixnode
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-network-statistics
|
||||
target/release/nym-cli
|
||||
retention-days: 30
|
||||
@@ -2,34 +2,20 @@ name: ci-build-upload-binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
add_tokio_unstable:
|
||||
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
enable_wireguard:
|
||||
description: 'Add --features wireguard'
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
schedule:
|
||||
- cron: '14 0 * * *'
|
||||
pull_request:
|
||||
paths:
|
||||
- "clients/**"
|
||||
- "common/**"
|
||||
- "explorer-api/**"
|
||||
- "gateway/**"
|
||||
- "integrations/**"
|
||||
- "mixnode/**"
|
||||
- "nym-api/**"
|
||||
- "nym-node/**"
|
||||
- "nym-outfox/**"
|
||||
- "nym-validator-rewarder/**"
|
||||
- "sdk/rust/nym-sdk/**"
|
||||
- "service-providers/**"
|
||||
- "tools/**"
|
||||
- 'clients/**'
|
||||
- 'common/**'
|
||||
- 'explorer-api/**'
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
- 'mixnode/**'
|
||||
- 'sdk/rust/nym-sdk/**'
|
||||
- 'service-providers/**'
|
||||
- 'nym-api/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'tools/nym-cli/**'
|
||||
- 'tools/ts-rs-cli/**'
|
||||
|
||||
jobs:
|
||||
publish-nym:
|
||||
@@ -56,18 +42,6 @@ jobs:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt update && sudo apt install libudev-dev
|
||||
|
||||
- name: Sets env vars for tokio if set in manual dispatch inputs
|
||||
run: |
|
||||
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
|
||||
|
||||
- name: Set CARGO_FEATURES
|
||||
run: |
|
||||
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
|
||||
if: >
|
||||
github.event_name == 'schedule' ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true)
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -77,40 +51,9 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release ${{ env.CARGO_FEATURES }}
|
||||
|
||||
- name: Install cargo-deb
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: cargo-deb
|
||||
|
||||
- name: Build deb packages
|
||||
shell: bash
|
||||
run: make deb
|
||||
|
||||
# If this was a manual workflow_dispatch, publish binaries.
|
||||
|
||||
- name: Upload Artifact
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-binaries-artifacts
|
||||
path: |
|
||||
target/release/nym-client
|
||||
target/release/nym-gateway
|
||||
target/release/nym-mixnode
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-network-statistics
|
||||
target/release/nym-cli
|
||||
retention-days: 30
|
||||
|
||||
# If this was a pull_request or nightly, upload to build server
|
||||
args: --workspace --release
|
||||
|
||||
- name: Prepare build output
|
||||
# if: github.event_name == 'schedule' || github.event_name == 'pull_request'
|
||||
shell: bash
|
||||
env:
|
||||
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
|
||||
@@ -124,10 +67,8 @@ jobs:
|
||||
cp target/release/nym-network-statistics $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
cp target/release/explorer-api $OUTPUT_DIR
|
||||
cp target/debian/*.deb $OUTPUT_DIR
|
||||
|
||||
- name: Deploy branch to CI www
|
||||
# if: github.event_name == 'schedule' || github.event_name == 'pull_request'
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
name: ci-cargo-deny
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
on: [workflow_dispatch]
|
||||
jobs:
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -10,7 +7,10 @@ jobs:
|
||||
matrix:
|
||||
checks:
|
||||
# - advisories
|
||||
- licenses bans sources
|
||||
- licenses
|
||||
- bans sources
|
||||
|
||||
continue-on-error: ${{ matrix.checks == 'licenses' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: install yarn in root
|
||||
run: cd ../.. && yarn install
|
||||
run: cd ../.. yarn install
|
||||
|
||||
- name: Install npm
|
||||
run: npm install
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: ci-nym-vpn-ui-js
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src/**'
|
||||
- 'nym-vpn/ui/package.json'
|
||||
- 'nym-vpn/ui/index.html'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn
|
||||
- name: Install dependencies
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn
|
||||
- name: Type-check
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn typecheck
|
||||
- name: Check lint
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn lint
|
||||
- name: Check formatting
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn fmt:check
|
||||
# - name: Run tests
|
||||
# working-directory: nym-vpn/ui
|
||||
# run: yarn test
|
||||
- name: Check build
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn build
|
||||
@@ -0,0 +1,63 @@
|
||||
name: ci-nym-vpn-ui-rust
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src-tauri/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-linux
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
CARGOTOML_PATH: ./nym-vpn/ui/src-tauri/Cargo.toml
|
||||
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 squashfs-tools libayatana-appindicator3-dev
|
||||
continue-on-error: true
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Prepare build
|
||||
run: mkdir nym-vpn/ui/dist
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --features custom-protocol
|
||||
|
||||
# - name: Run all tests
|
||||
# uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: test
|
||||
# args: --manifest-path ${{ env.CARGOTOML_PATH }}
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all -- --check
|
||||
|
||||
- name: Annotate with clippy checks
|
||||
uses: actions-rs/clippy-check@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features --all-targets -- -D warnings
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-binaries
|
||||
name: Publish Nym binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-connect-macos
|
||||
name: Publish Nym Connect - desktop (MacOS)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-connect-ubuntu
|
||||
name: Publish Nym Connect - desktop (Ubuntu)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-connect-win10
|
||||
name: Publish Nym Connect - desktop (Windows 10)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-contracts
|
||||
name: Build release of Nym smart contracts
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-wallet-macos
|
||||
name: Publish Nym Wallet (MacOS)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-wallet-ubuntu
|
||||
name: Publish Nym Wallet (Ubuntu)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-wallet-win10
|
||||
name: Publish Nym Wallet (Windows 10)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: publish-sdk-npm
|
||||
name: Publish Typescript SDK
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: release-calculate-hash
|
||||
name: Releases - calculate file hashes
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
@@ -187,16 +187,6 @@ dependencies = [
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a93b8a41dbe230ad5087cc721f8d41611de654542180586b315d9f4cf6b72bef"
|
||||
dependencies = [
|
||||
"psl",
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
@@ -1471,7 +1461,7 @@ version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e959d788268e3bf9d35ace83e81b124190378e4c91c9067524675e33394b8ba"
|
||||
dependencies = [
|
||||
"crossterm 0.26.1",
|
||||
"crossterm",
|
||||
"strum 0.24.1",
|
||||
"strum_macros 0.24.3",
|
||||
"unicode-width",
|
||||
@@ -1569,12 +1559,6 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
|
||||
|
||||
[[package]]
|
||||
name = "const-str"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
version = "0.2.32"
|
||||
@@ -1620,9 +1604,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8"
|
||||
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
@@ -1932,22 +1916,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot 0.12.1",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.26.1"
|
||||
@@ -2034,27 +2002,6 @@ dependencies = [
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.26"
|
||||
@@ -4034,16 +3981,6 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
@@ -4160,22 +4097,6 @@ dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inquire"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crossterm 0.25.0",
|
||||
"dyn-clone",
|
||||
"lazy_static",
|
||||
"newline-converter",
|
||||
"thiserror",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -5849,15 +5770,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "newline-converter"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.3"
|
||||
@@ -6138,7 +6050,6 @@ dependencies = [
|
||||
"clap 4.4.7",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"const-str",
|
||||
"log",
|
||||
"opentelemetry",
|
||||
"opentelemetry-jaeger",
|
||||
@@ -6181,7 +6092,6 @@ dependencies = [
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"dotenvy",
|
||||
"inquire",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-cli-commands",
|
||||
@@ -6207,11 +6117,9 @@ dependencies = [
|
||||
"comfy-table",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"cosmwasm-std",
|
||||
"csv",
|
||||
"cw-utils",
|
||||
"handlebars",
|
||||
"humantime-serde",
|
||||
"inquire",
|
||||
"k256",
|
||||
"log",
|
||||
"nym-bandwidth-controller",
|
||||
@@ -6314,7 +6222,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"si-scale",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
@@ -6732,13 +6639,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bytes",
|
||||
"nym-bin-common",
|
||||
"nym-sphinx",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6746,27 +6650,21 @@ name = "nym-ip-packet-router"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bs58 0.4.0",
|
||||
"bytes",
|
||||
"clap 4.4.7",
|
||||
"etherparse",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-client-core",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
"nym-exit-policy",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-network-defaults",
|
||||
"nym-network-requester",
|
||||
"nym-sdk",
|
||||
"nym-service-providers-common",
|
||||
"nym-sphinx",
|
||||
"nym-task",
|
||||
"nym-tun",
|
||||
"nym-types",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
@@ -6777,7 +6675,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tun",
|
||||
"tokio-util",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -6926,7 +6823,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"dotenvy",
|
||||
"hex-literal",
|
||||
"log",
|
||||
"once_cell",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -6938,7 +6834,6 @@ dependencies = [
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.32"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
"async-file-watcher",
|
||||
"async-trait",
|
||||
@@ -7835,9 +7730,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "okapi"
|
||||
version = "0.7.0"
|
||||
version = "0.7.0-rc.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64853d7ab065474e87696f7601cee817d200e86c42e04004e005cb3e20c3c5"
|
||||
checksum = "ce66b6366e049880a35c378123fddb630b1a1a3c37fa1ca70caaf4a09f6e2893"
|
||||
dependencies = [
|
||||
"log",
|
||||
"schemars",
|
||||
@@ -8166,7 +8061,7 @@ checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c"
|
||||
dependencies = [
|
||||
"inlinable_string",
|
||||
"pear_codegen",
|
||||
"yansi",
|
||||
"yansi 1.0.0-rc.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8543,7 +8438,7 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"version_check",
|
||||
"yansi",
|
||||
"yansi 1.0.0-rc.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8667,29 +8562,15 @@ dependencies = [
|
||||
"prost 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl"
|
||||
version = "2.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "383703acfc34f7a00724846c14dc5ea4407c59e5aedcbbb18a1c0c1a23fe5013"
|
||||
dependencies = [
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl-types"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "publicsuffix"
|
||||
version = "2.2.3"
|
||||
version = "1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457"
|
||||
checksum = "95b4ce31ff0a27d93c8de1849cf58162283752f065a90d508f1105fa6c9a213f"
|
||||
dependencies = [
|
||||
"idna 0.3.0",
|
||||
"psl-types",
|
||||
"idna 0.2.3",
|
||||
"native-tls",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9292,9 +9173,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket"
|
||||
version = "0.5.0"
|
||||
version = "0.5.0-rc.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150"
|
||||
checksum = "58734f7401ae5cfd129685b48f61182331745b357b96f2367f01aebaf1cc9cc9"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -9304,7 +9185,8 @@ dependencies = [
|
||||
"either",
|
||||
"figment",
|
||||
"futures",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 1.9.3",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"memchr",
|
||||
"multer",
|
||||
@@ -9325,33 +9207,30 @@ dependencies = [
|
||||
"tokio-util",
|
||||
"ubyte",
|
||||
"version_check",
|
||||
"yansi",
|
||||
"yansi 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_codegen"
|
||||
version = "0.5.0"
|
||||
version = "0.5.0-rc.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c"
|
||||
checksum = "7093353f14228c744982e409259fb54878ba9563d08214f2d880d59ff2fc508b"
|
||||
dependencies = [
|
||||
"devise",
|
||||
"glob",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 1.9.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rocket_http",
|
||||
"syn 2.0.38",
|
||||
"unicode-xid",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_cors"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfac3a1df83f8d4fc96aa41dba3b86c786417b7fc0f52ec76295df2ba781aa69"
|
||||
version = "0.5.2"
|
||||
source = "git+https://github.com/lawliet89/rocket_cors?rev=dfd3662c49e2f6fc37df35091cb94d82f7fb5915#dfd3662c49e2f6fc37df35091cb94d82f7fb5915"
|
||||
dependencies = [
|
||||
"http",
|
||||
"log",
|
||||
"regex",
|
||||
"rocket",
|
||||
@@ -9364,16 +9243,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_http"
|
||||
version = "0.5.0"
|
||||
version = "0.5.0-rc.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e"
|
||||
checksum = "936012c99162a03a67f37f9836d5f938f662e26f2717809761a9ac46432090f4"
|
||||
dependencies = [
|
||||
"cookie 0.18.0",
|
||||
"cookie 0.17.0",
|
||||
"either",
|
||||
"futures",
|
||||
"http",
|
||||
"hyper",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 1.9.3",
|
||||
"log",
|
||||
"memchr",
|
||||
"pear",
|
||||
@@ -9391,10 +9270,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_okapi"
|
||||
version = "0.8.0"
|
||||
version = "0.8.0-rc.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e059407ecef9ee2f071fc971e10444fcf942149deb028879d6d8ca61a7ce9edc"
|
||||
checksum = "742098674565c8f0c35c77444f90344aafedebb71cfee9cdbf0185acc6b9cdb7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"log",
|
||||
"okapi",
|
||||
"rocket",
|
||||
@@ -9406,9 +9286,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_okapi_codegen"
|
||||
version = "0.8.0"
|
||||
version = "0.8.0-rc.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfb96114e69e5d7f80bfa0948cbc0120016e9b460954abe9eed37e9a2ad3f999"
|
||||
checksum = "8c43f8edc57d88750a220b0ec1870a36c1106204ec99cc35131b49de3b954a4a"
|
||||
dependencies = [
|
||||
"darling 0.13.4",
|
||||
"proc-macro2",
|
||||
@@ -9724,9 +9604,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.16"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
|
||||
checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"indexmap 1.9.3",
|
||||
@@ -9737,9 +9617,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.16"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
|
||||
checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -10137,12 +10017,6 @@ dependencies = [
|
||||
"dirs 4.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "si-scale"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44beb68bf488343b13ddbd74d1d5d5e6559a58b6dfaee74eb8d5ed4f7ed7666f"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
@@ -10502,9 +10376,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "state"
|
||||
version = "0.6.0"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
|
||||
checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b"
|
||||
dependencies = [
|
||||
"loom",
|
||||
]
|
||||
@@ -12756,14 +12630,17 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.0-rc.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yasna"
|
||||
|
||||
@@ -128,7 +128,7 @@ default-members = [
|
||||
"nym-validator-rewarder",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles", "sdk/ffi/cpp"]
|
||||
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles"]
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Nym Technologies SA"]
|
||||
|
||||
@@ -12,7 +12,6 @@ help:
|
||||
@echo " clippy: run clippy for all workspaces"
|
||||
@echo " test: run clippy, unit tests, and formatting."
|
||||
@echo " test-all: like test, but also includes the expensive tests"
|
||||
@echo " deb: build debian packages
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Meta targets
|
||||
@@ -158,12 +157,6 @@ build-explorer-api:
|
||||
build-nym-cli:
|
||||
cargo build -p nym-cli --release
|
||||
|
||||
build-nym-gateway:
|
||||
cargo build -p nym-gateway --release
|
||||
|
||||
build-nym-mixnode:
|
||||
cargo build -p nym-mixnode --release
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Misc
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -176,13 +169,6 @@ run-api-tests:
|
||||
cd nym-api/tests/functional_test && yarn test:qa
|
||||
|
||||
# Build debian package, and update PPA
|
||||
deb-mixnode: build-nym-mixnode
|
||||
cargo deb -p nym-mixnode
|
||||
|
||||
deb-gateway: build-nym-gateway
|
||||
cargo deb -p nym-gateway
|
||||
|
||||
deb-cli: build-nym-cli
|
||||
cargo deb -p nym-cli
|
||||
|
||||
deb: deb-mixnode deb-gateway deb-cli
|
||||
# Requires base64 encode GPG key to be set up in environment PPA_SIGNING_KEY
|
||||
deb:
|
||||
scripts/ppa.sh
|
||||
|
||||
@@ -51,7 +51,7 @@ impl InitialisableClient for NativeClientInit {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Clone, Debug)]
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
#[command(flatten)]
|
||||
common_args: CommonClientInitArgs,
|
||||
|
||||
@@ -51,7 +51,7 @@ impl InitialisableClient for Socks5ClientInit {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Clone, Debug)]
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
#[command(flatten)]
|
||||
common_args: CommonClientInitArgs,
|
||||
|
||||
@@ -9,7 +9,6 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2"
|
||||
const-str = "0.5.6"
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
clap_complete = "4.0"
|
||||
clap_complete_fig = "4.0"
|
||||
|
||||
@@ -42,20 +42,12 @@ pub struct BinaryBuildInformation {
|
||||
|
||||
// VERGEN_CARGO_DEBUG
|
||||
/// Provides the cargo debug mode that was used for the build.
|
||||
// NOTE: keep the old name cargo_profile instead of cargo_debug for backwards compatibility
|
||||
pub cargo_profile: &'static str,
|
||||
pub cargo_debug: &'static str,
|
||||
}
|
||||
|
||||
impl BinaryBuildInformation {
|
||||
// explicitly require the build_version to be passed as it's binary specific
|
||||
pub const fn new(binary_name: &'static str, build_version: &'static str) -> Self {
|
||||
let cargo_debug = env!("VERGEN_CARGO_DEBUG");
|
||||
let cargo_profile = if const_str::equal!(cargo_debug, "true") {
|
||||
"debug"
|
||||
} else {
|
||||
"release"
|
||||
};
|
||||
|
||||
BinaryBuildInformation {
|
||||
binary_name,
|
||||
build_timestamp: env!("VERGEN_BUILD_TIMESTAMP"),
|
||||
@@ -65,7 +57,7 @@ impl BinaryBuildInformation {
|
||||
commit_branch: env!("VERGEN_GIT_BRANCH"),
|
||||
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
|
||||
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
|
||||
cargo_profile,
|
||||
cargo_debug: env!("VERGEN_CARGO_DEBUG"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +71,7 @@ impl BinaryBuildInformation {
|
||||
commit_branch: self.commit_branch.to_owned(),
|
||||
rustc_version: self.rustc_version.to_owned(),
|
||||
rustc_channel: self.rustc_channel.to_owned(),
|
||||
cargo_profile: self.cargo_profile.to_owned(),
|
||||
cargo_debug: self.cargo_debug.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,8 +117,7 @@ pub struct BinaryBuildInformationOwned {
|
||||
|
||||
// VERGEN_CARGO_DEBUG
|
||||
/// Provides the cargo debug mode that was used for the build.
|
||||
// NOTE: keep the old name cargo_profile instead of cargo_debug for backwards compatibility
|
||||
pub cargo_profile: String,
|
||||
pub cargo_debug: String,
|
||||
}
|
||||
|
||||
impl Display for BinaryBuildInformationOwned {
|
||||
@@ -160,8 +151,8 @@ impl Display for BinaryBuildInformationOwned {
|
||||
self.rustc_version,
|
||||
"rustc Channel:",
|
||||
self.rustc_channel,
|
||||
"cargo Profile:",
|
||||
self.cargo_profile,
|
||||
"cargo Debug:",
|
||||
self.cargo_debug,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
|
||||
nym-task = { path = "../task" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
si-scale = "0.2.2"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
|
||||
version = "0.1.11"
|
||||
|
||||
@@ -109,8 +109,6 @@ pub async fn initialise_client<C>(
|
||||
) -> Result<InitResultsWithConfig<C::Config>, C::Error>
|
||||
where
|
||||
C: InitialisableClient,
|
||||
<C as InitialisableClient>::Config: std::fmt::Debug,
|
||||
<C as InitialisableClient>::InitArgs: std::fmt::Debug,
|
||||
{
|
||||
info!("initialising {} client", C::NAME);
|
||||
|
||||
@@ -142,32 +140,17 @@ where
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = common_args.gateway;
|
||||
log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}");
|
||||
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(common_args.latency_based_selection),
|
||||
false,
|
||||
);
|
||||
log::debug!("Gateway selection specification: {selection_spec:?}");
|
||||
|
||||
// Load and potentially override config
|
||||
log::debug!("Init arguments: {init_args:#?}");
|
||||
let config = C::construct_config(&init_args);
|
||||
log::debug!("Constructed config: {config:#?}");
|
||||
let paths = config.common_paths();
|
||||
let core = config.core_config();
|
||||
|
||||
log::info!(
|
||||
"Using nym-api: {}",
|
||||
core.client
|
||||
.nym_api_urls
|
||||
.iter()
|
||||
.map(|url| url.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(",")
|
||||
);
|
||||
|
||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||
// one but with keys kept, or reusing the gateway configuration.
|
||||
let key_store = OnDiskKeys::new(paths.keys.clone());
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::packet_statistics_control::PacketStatisticsReporter;
|
||||
use super::received_buffer::ReceivedBufferMessage;
|
||||
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
|
||||
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
|
||||
@@ -11,7 +10,6 @@ use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputM
|
||||
use crate::client::key_manager::persistence::KeyStore;
|
||||
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
|
||||
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
|
||||
use crate::client::packet_statistics_control::PacketStatisticsControl;
|
||||
use crate::client::real_messages_control;
|
||||
use crate::client::real_messages_control::RealMessagesController;
|
||||
use crate::client::received_buffer::{
|
||||
@@ -52,7 +50,6 @@ use nym_topology::provider_trait::TopologyProvider;
|
||||
use nym_topology::HardcodedTopologyProvider;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use std::fmt::Debug;
|
||||
use std::os::fd::RawFd;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
@@ -257,7 +254,6 @@ where
|
||||
self_address: Recipient,
|
||||
topology_accessor: TopologyAccessor,
|
||||
mix_tx: BatchMixMessageSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
shutdown: TaskClient,
|
||||
) {
|
||||
info!("Starting loop cover traffic stream...");
|
||||
@@ -270,7 +266,6 @@ where
|
||||
topology_accessor,
|
||||
debug_config.traffic,
|
||||
debug_config.cover_traffic,
|
||||
stats_tx,
|
||||
);
|
||||
|
||||
stream.start_with_shutdown(shutdown);
|
||||
@@ -290,7 +285,6 @@ where
|
||||
client_connection_rx: ConnectionCommandReceiver,
|
||||
shutdown: TaskClient,
|
||||
packet_type: PacketType,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) {
|
||||
info!("Starting real traffic stream...");
|
||||
|
||||
@@ -305,7 +299,6 @@ where
|
||||
reply_controller_receiver,
|
||||
lane_queue_lengths,
|
||||
client_connection_rx,
|
||||
stats_tx,
|
||||
)
|
||||
.start_with_shutdown(shutdown, packet_type);
|
||||
}
|
||||
@@ -319,7 +312,6 @@ where
|
||||
reply_key_storage: SentReplyKeys,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
shutdown: TaskClient,
|
||||
packet_statistics_control: PacketStatisticsReporter,
|
||||
) {
|
||||
info!("Starting received messages buffer controller...");
|
||||
let controller: ReceivedMessagesBufferController<SphinxMessageReceiver> =
|
||||
@@ -329,7 +321,6 @@ where
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
packet_statistics_control,
|
||||
);
|
||||
controller.start_with_shutdown(shutdown)
|
||||
}
|
||||
@@ -515,13 +506,6 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_packet_statistics_control(shutdown: TaskClient) -> PacketStatisticsReporter {
|
||||
info!("Starting packet statistics control...");
|
||||
let (packet_statistics_control, packet_stats_reporter) = PacketStatisticsControl::new();
|
||||
packet_statistics_control.start_with_shutdown(shutdown);
|
||||
packet_stats_reporter
|
||||
}
|
||||
|
||||
fn start_mix_traffic_controller(
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
shutdown: TaskClient,
|
||||
@@ -649,9 +633,6 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
let packet_stats_reporter =
|
||||
Self::start_packet_statistics_control(shutdown.fork("packet_statistics_control"));
|
||||
|
||||
let gateway_packet_router = PacketRouter::new(
|
||||
ack_sender,
|
||||
mixnet_messages_sender,
|
||||
@@ -681,10 +662,8 @@ where
|
||||
reply_storage.key_storage(),
|
||||
reply_controller_sender.clone(),
|
||||
shutdown.fork("received_messages_buffer"),
|
||||
packet_stats_reporter.clone(),
|
||||
);
|
||||
|
||||
let gateway_fd = gateway_transceiver.ws_fd();
|
||||
// The message_sender is the transmitter for any component generating sphinx packets
|
||||
// that are to be sent to the mixnet. They are used by cover traffic stream and real
|
||||
// traffic stream.
|
||||
@@ -721,7 +700,6 @@ where
|
||||
client_connection_rx,
|
||||
shutdown.fork("real_traffic_controller"),
|
||||
self.config.debug.traffic.packet_type,
|
||||
packet_stats_reporter.clone(),
|
||||
);
|
||||
|
||||
if !self
|
||||
@@ -736,7 +714,6 @@ where
|
||||
self_address,
|
||||
shared_topology_accessor.clone(),
|
||||
message_sender,
|
||||
packet_stats_reporter,
|
||||
shutdown.fork("cover_traffic_stream"),
|
||||
);
|
||||
}
|
||||
@@ -757,7 +734,6 @@ where
|
||||
received_buffer_request_sender,
|
||||
},
|
||||
},
|
||||
gateway_fd,
|
||||
client_state: ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
@@ -773,7 +749,6 @@ pub struct BaseClient {
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
pub gateway_fd: Option<RawFd>,
|
||||
|
||||
pub task_handle: TaskHandle,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::mix_traffic::BatchMixMessageSender;
|
||||
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
|
||||
use crate::client::topology_control::TopologyAccessor;
|
||||
use crate::{config, spawn_future};
|
||||
use futures::task::{Context, Poll};
|
||||
@@ -62,8 +61,6 @@ where
|
||||
secondary_packet_size: Option<PacketSize>,
|
||||
|
||||
packet_type: PacketType,
|
||||
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
impl<R> Stream for LoopCoverTrafficStream<R>
|
||||
@@ -100,8 +97,7 @@ where
|
||||
// obviously when we finally make shared rng that is on 'higher' level, this should become
|
||||
// generic `R`
|
||||
impl LoopCoverTrafficStream<OsRng> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
ack_key: Arc<AckKey>,
|
||||
average_ack_delay: Duration,
|
||||
mix_tx: BatchMixMessageSender,
|
||||
@@ -109,7 +105,6 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
topology_access: TopologyAccessor,
|
||||
traffic_config: config::Traffic,
|
||||
cover_config: config::CoverTraffic,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let rng = OsRng;
|
||||
|
||||
@@ -127,7 +122,6 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
primary_packet_size: traffic_config.primary_packet_size,
|
||||
secondary_packet_size: traffic_config.secondary_packet_size,
|
||||
packet_type: traffic_config.packet_type,
|
||||
stats_tx,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,10 +191,6 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
log::warn!("Failed to send cover message - channel closed");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.stats_tx.report(PacketStatisticsEvent::CoverPacketSent(
|
||||
cover_traffic_packet_size.size(),
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: I'm not entirely sure whether this is really required, because I'm not 100%
|
||||
|
||||
@@ -8,7 +8,6 @@ use nym_gateway_client::GatewayClient;
|
||||
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use std::fmt::Debug;
|
||||
use std::os::fd::RawFd;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -26,7 +25,6 @@ fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGate
|
||||
/// This combines combines the functionalities of being able to send and receive mix packets.
|
||||
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
|
||||
fn gateway_identity(&self) -> identity::PublicKey;
|
||||
fn ws_fd(&self) -> Option<RawFd>;
|
||||
}
|
||||
|
||||
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
|
||||
@@ -68,9 +66,6 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
(**self).gateway_identity()
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
(**self).ws_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -117,9 +112,6 @@ where
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
self.gateway_client.gateway_identity()
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
self.gateway_client.ws_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -195,9 +187,6 @@ mod nonwasm_sealed {
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
self.local_identity
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -270,7 +259,4 @@ impl GatewayTransceiver for MockGateway {
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
self.dummy_identity
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ pub(crate) mod helpers;
|
||||
pub mod inbound_messages;
|
||||
pub mod key_manager;
|
||||
pub mod mix_traffic;
|
||||
pub(crate) mod packet_statistics_control;
|
||||
pub mod real_messages_control;
|
||||
pub mod received_buffer;
|
||||
pub mod replies;
|
||||
|
||||
@@ -1,503 +0,0 @@
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use si_scale::helpers::bibytes2;
|
||||
|
||||
use crate::spawn_future;
|
||||
|
||||
// Time interval between reporting packet statistics
|
||||
const PACKET_REPORT_INTERVAL_SECS: u64 = 2;
|
||||
// Interval for taking snapshots of the packet statistics
|
||||
const SNAPSHOT_INTERVAL_MS: u64 = 500;
|
||||
// When computing rates, we include snapshots that are up to this old. We set it to some odd number
|
||||
// a tad larger than an integer number of snapshot intervals, so that we don't have to worry about
|
||||
// threshold effects.
|
||||
// Also, set it larger than the packet report interval so that we don't miss notable singular events
|
||||
const RECORDING_WINDOW_MS: u64 = 2300;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
struct PacketStatistics {
|
||||
// Sent
|
||||
real_packets_sent: u64,
|
||||
real_packets_sent_size: usize,
|
||||
cover_packets_sent: u64,
|
||||
cover_packets_sent_size: usize,
|
||||
|
||||
// Received
|
||||
real_packets_received: u64,
|
||||
real_packets_received_size: usize,
|
||||
cover_packets_received: u64,
|
||||
cover_packets_received_size: usize,
|
||||
|
||||
// Acks
|
||||
total_acks_received: u64,
|
||||
total_acks_received_size: usize,
|
||||
real_acks_received: u64,
|
||||
real_acks_received_size: usize,
|
||||
cover_acks_received: u64,
|
||||
cover_acks_received_size: usize,
|
||||
|
||||
// Types of packets queued
|
||||
// TODO: track the type sent instead
|
||||
real_packets_queued: u64,
|
||||
retransmissions_queued: u64,
|
||||
reply_surbs_queued: u64,
|
||||
additional_reply_surbs_queued: u64,
|
||||
}
|
||||
|
||||
impl PacketStatistics {
|
||||
fn handle_event(&mut self, event: PacketStatisticsEvent) {
|
||||
match event {
|
||||
PacketStatisticsEvent::RealPacketSent(packet_size) => {
|
||||
self.real_packets_sent += 1;
|
||||
self.real_packets_sent_size += packet_size;
|
||||
}
|
||||
PacketStatisticsEvent::CoverPacketSent(packet_size) => {
|
||||
self.cover_packets_sent += 1;
|
||||
self.cover_packets_sent_size += packet_size;
|
||||
}
|
||||
PacketStatisticsEvent::RealPacketReceived(packet_size) => {
|
||||
self.real_packets_received += 1;
|
||||
self.real_packets_received_size += packet_size;
|
||||
}
|
||||
PacketStatisticsEvent::CoverPacketReceived(packet_size) => {
|
||||
self.cover_packets_received += 1;
|
||||
self.cover_packets_received_size += packet_size;
|
||||
}
|
||||
PacketStatisticsEvent::AckReceived(packet_size) => {
|
||||
self.total_acks_received += 1;
|
||||
self.total_acks_received_size += packet_size;
|
||||
}
|
||||
PacketStatisticsEvent::RealAckReceived(packet_size) => {
|
||||
self.real_acks_received += 1;
|
||||
self.real_acks_received_size += packet_size;
|
||||
}
|
||||
PacketStatisticsEvent::CoverAckReceived(packet_size) => {
|
||||
self.cover_acks_received += 1;
|
||||
self.cover_acks_received_size += packet_size;
|
||||
}
|
||||
PacketStatisticsEvent::RealPacketQueued => {
|
||||
self.real_packets_queued += 1;
|
||||
}
|
||||
PacketStatisticsEvent::RetransmissionQueued => {
|
||||
self.retransmissions_queued += 1;
|
||||
}
|
||||
PacketStatisticsEvent::ReplySurbRequestQueued => {
|
||||
self.reply_surbs_queued += 1;
|
||||
}
|
||||
PacketStatisticsEvent::AdditionalReplySurbRequestQueued => {
|
||||
self.additional_reply_surbs_queued += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn summary(&self) -> (String, String) {
|
||||
(
|
||||
format!(
|
||||
"packets sent: {} (real: {}, cover: {}, retransmissions: {})",
|
||||
self.real_packets_sent + self.cover_packets_sent,
|
||||
self.real_packets_sent,
|
||||
self.cover_packets_sent,
|
||||
self.retransmissions_queued,
|
||||
),
|
||||
format!(
|
||||
"packets received: {}, (real: {}, cover: {}, acks: {}, acks for cover: {})",
|
||||
self.real_packets_received + self.cover_packets_received,
|
||||
self.real_packets_received,
|
||||
self.cover_packets_received,
|
||||
self.real_acks_received,
|
||||
self.cover_acks_received,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for PacketStatistics {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
real_packets_sent: self.real_packets_sent - rhs.real_packets_sent,
|
||||
real_packets_sent_size: self.real_packets_sent_size - rhs.real_packets_sent_size,
|
||||
cover_packets_sent: self.cover_packets_sent - rhs.cover_packets_sent,
|
||||
cover_packets_sent_size: self.cover_packets_sent_size - rhs.cover_packets_sent_size,
|
||||
|
||||
real_packets_received: self.real_packets_received - rhs.real_packets_received,
|
||||
real_packets_received_size: self.real_packets_received_size
|
||||
- rhs.real_packets_received_size,
|
||||
cover_packets_received: self.cover_packets_received - rhs.cover_packets_received,
|
||||
cover_packets_received_size: self.cover_packets_received_size
|
||||
- rhs.cover_packets_received_size,
|
||||
|
||||
total_acks_received: self.total_acks_received - rhs.total_acks_received,
|
||||
total_acks_received_size: self.total_acks_received_size - rhs.total_acks_received_size,
|
||||
real_acks_received: self.real_acks_received - rhs.real_acks_received,
|
||||
real_acks_received_size: self.real_acks_received_size - rhs.real_acks_received_size,
|
||||
cover_acks_received: self.cover_acks_received - rhs.cover_acks_received,
|
||||
cover_acks_received_size: self.cover_acks_received_size - rhs.cover_acks_received_size,
|
||||
|
||||
real_packets_queued: self.real_packets_queued - rhs.real_packets_queued,
|
||||
retransmissions_queued: self.retransmissions_queued - rhs.retransmissions_queued,
|
||||
reply_surbs_queued: self.reply_surbs_queued - rhs.reply_surbs_queued,
|
||||
additional_reply_surbs_queued: self.additional_reply_surbs_queued
|
||||
- rhs.additional_reply_surbs_queued,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PacketRates {
|
||||
real_packets_sent: f64,
|
||||
real_packets_sent_size: f64,
|
||||
cover_packets_sent: f64,
|
||||
cover_packets_sent_size: f64,
|
||||
|
||||
real_packets_received: f64,
|
||||
real_packets_received_size: f64,
|
||||
cover_packets_received: f64,
|
||||
cover_packets_received_size: f64,
|
||||
|
||||
total_acks_received: f64,
|
||||
total_acks_received_size: f64,
|
||||
real_acks_received: f64,
|
||||
real_acks_received_size: f64,
|
||||
cover_acks_received: f64,
|
||||
cover_acks_received_size: f64,
|
||||
|
||||
real_packets_queued: f64,
|
||||
retransmissions_queued: f64,
|
||||
reply_surbs_queued: f64,
|
||||
additional_reply_surbs_queued: f64,
|
||||
}
|
||||
|
||||
impl From<PacketStatistics> for PacketRates {
|
||||
fn from(stats: PacketStatistics) -> Self {
|
||||
Self {
|
||||
real_packets_sent: stats.real_packets_sent as f64,
|
||||
real_packets_sent_size: stats.real_packets_sent_size as f64,
|
||||
cover_packets_sent: stats.cover_packets_sent as f64,
|
||||
cover_packets_sent_size: stats.cover_packets_sent_size as f64,
|
||||
|
||||
real_packets_received: stats.real_packets_received as f64,
|
||||
real_packets_received_size: stats.real_packets_received_size as f64,
|
||||
cover_packets_received: stats.cover_packets_received as f64,
|
||||
cover_packets_received_size: stats.cover_packets_received_size as f64,
|
||||
|
||||
total_acks_received: stats.total_acks_received as f64,
|
||||
total_acks_received_size: stats.total_acks_received_size as f64,
|
||||
real_acks_received: stats.real_acks_received as f64,
|
||||
real_acks_received_size: stats.real_acks_received_size as f64,
|
||||
cover_acks_received: stats.cover_acks_received as f64,
|
||||
cover_acks_received_size: stats.cover_acks_received_size as f64,
|
||||
|
||||
real_packets_queued: stats.real_packets_queued as f64,
|
||||
retransmissions_queued: stats.retransmissions_queued as f64,
|
||||
reply_surbs_queued: stats.reply_surbs_queued as f64,
|
||||
additional_reply_surbs_queued: stats.additional_reply_surbs_queued as f64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for PacketRates {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
real_packets_sent: self.real_packets_sent - rhs.real_packets_sent,
|
||||
real_packets_sent_size: self.real_packets_sent_size - rhs.real_packets_sent_size,
|
||||
cover_packets_sent: self.cover_packets_sent - rhs.cover_packets_sent,
|
||||
cover_packets_sent_size: self.cover_packets_sent_size - rhs.cover_packets_sent_size,
|
||||
|
||||
real_packets_received: self.real_packets_received - rhs.real_packets_received,
|
||||
real_packets_received_size: self.real_packets_received_size
|
||||
- rhs.real_packets_received_size,
|
||||
cover_packets_received: self.cover_packets_received - rhs.cover_packets_received,
|
||||
cover_packets_received_size: self.cover_packets_received_size
|
||||
- rhs.cover_packets_received_size,
|
||||
|
||||
total_acks_received: self.total_acks_received - rhs.total_acks_received,
|
||||
total_acks_received_size: self.total_acks_received_size - rhs.total_acks_received_size,
|
||||
real_acks_received: self.real_acks_received - rhs.real_acks_received,
|
||||
real_acks_received_size: self.real_acks_received_size - rhs.real_acks_received_size,
|
||||
cover_acks_received: self.cover_acks_received - rhs.cover_acks_received,
|
||||
cover_acks_received_size: self.cover_acks_received_size - rhs.cover_acks_received_size,
|
||||
|
||||
real_packets_queued: self.real_packets_queued - rhs.real_packets_queued,
|
||||
retransmissions_queued: self.retransmissions_queued - rhs.retransmissions_queued,
|
||||
reply_surbs_queued: self.reply_surbs_queued - rhs.reply_surbs_queued,
|
||||
additional_reply_surbs_queued: self.additional_reply_surbs_queued
|
||||
- rhs.additional_reply_surbs_queued,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Div<f64> for PacketRates {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: f64) -> Self::Output {
|
||||
Self {
|
||||
real_packets_sent: self.real_packets_sent / rhs,
|
||||
real_packets_sent_size: self.real_packets_sent_size / rhs,
|
||||
cover_packets_sent: self.cover_packets_sent / rhs,
|
||||
cover_packets_sent_size: self.cover_packets_sent_size / rhs,
|
||||
|
||||
real_packets_received: self.real_packets_received / rhs,
|
||||
real_packets_received_size: self.real_packets_received_size / rhs,
|
||||
cover_packets_received: self.cover_packets_received / rhs,
|
||||
cover_packets_received_size: self.cover_packets_received_size / rhs,
|
||||
|
||||
total_acks_received: self.total_acks_received / rhs,
|
||||
total_acks_received_size: self.total_acks_received_size / rhs,
|
||||
real_acks_received: self.real_acks_received / rhs,
|
||||
real_acks_received_size: self.real_acks_received_size / rhs,
|
||||
cover_acks_received: self.cover_acks_received / rhs,
|
||||
cover_acks_received_size: self.cover_acks_received_size / rhs,
|
||||
|
||||
real_packets_queued: self.real_packets_queued / rhs,
|
||||
retransmissions_queued: self.retransmissions_queued / rhs,
|
||||
reply_surbs_queued: self.reply_surbs_queued / rhs,
|
||||
additional_reply_surbs_queued: self.additional_reply_surbs_queued / rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketRates {
|
||||
fn summary(&self) -> String {
|
||||
format!(
|
||||
"down: {}/s, up: {}/s (cover down: {}/s, cover up: {}/s)",
|
||||
bibytes2(self.real_packets_received_size),
|
||||
bibytes2(self.real_packets_sent_size),
|
||||
bibytes2(self.cover_packets_received_size),
|
||||
bibytes2(self.cover_packets_sent_size),
|
||||
)
|
||||
}
|
||||
|
||||
fn detailed_summary(&self) -> String {
|
||||
format!(
|
||||
"RX: {:.1} mixpkt/s, {}/s (real: {}/s, acks: {}/s), TX: {:.1} mixpkt/s, {}/s (real: {}/s)",
|
||||
self.real_packets_received + self.cover_packets_received,
|
||||
bibytes2(self.real_packets_received_size + self.cover_packets_received_size),
|
||||
bibytes2(self.real_packets_received_size),
|
||||
bibytes2(self.total_acks_received_size),
|
||||
self.real_packets_sent + self.cover_packets_sent,
|
||||
bibytes2(self.real_packets_sent_size + self.cover_packets_sent_size),
|
||||
bibytes2(self.real_packets_sent_size),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PacketStatisticsEvent {
|
||||
// The real packets sent. Recall that acks are sent by the gateway, so it's not included here.
|
||||
RealPacketSent(usize),
|
||||
// The cover packets sent
|
||||
CoverPacketSent(usize),
|
||||
|
||||
// Real packets received
|
||||
RealPacketReceived(usize),
|
||||
// Cover packets received
|
||||
CoverPacketReceived(usize),
|
||||
|
||||
// Ack of any type received. This is mostly used as a consistency check, and should be the sum
|
||||
// of real and cover acks received.
|
||||
AckReceived(usize),
|
||||
// Out of the total acks received, this is the subset of those that were real
|
||||
RealAckReceived(usize),
|
||||
// Out of the total acks received, this is the subset of those that were for cover traffic
|
||||
CoverAckReceived(usize),
|
||||
|
||||
// Types of packets queued
|
||||
RealPacketQueued,
|
||||
RetransmissionQueued,
|
||||
ReplySurbRequestQueued,
|
||||
AdditionalReplySurbRequestQueued,
|
||||
}
|
||||
|
||||
type PacketStatisticsReceiver = tokio::sync::mpsc::UnboundedReceiver<PacketStatisticsEvent>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct PacketStatisticsReporter {
|
||||
stats_tx: tokio::sync::mpsc::UnboundedSender<PacketStatisticsEvent>,
|
||||
}
|
||||
|
||||
impl PacketStatisticsReporter {
|
||||
pub(crate) fn new(stats_tx: tokio::sync::mpsc::UnboundedSender<PacketStatisticsEvent>) -> Self {
|
||||
Self { stats_tx }
|
||||
}
|
||||
|
||||
pub(crate) fn report(&self, event: PacketStatisticsEvent) {
|
||||
self.stats_tx.send(event).unwrap_or_else(|err| {
|
||||
log::error!("Failed to report packet stat: {:?}", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PacketStatisticsControl {
|
||||
// Incoming packet stats events from other tasks
|
||||
stats_rx: PacketStatisticsReceiver,
|
||||
|
||||
// Keep track of packet statistics over time
|
||||
stats: PacketStatistics,
|
||||
|
||||
// We keep snapshots of the statistics over time so we can compute rates, and also keeping the
|
||||
// full history allows for some more fancy averaging if we want to do that.
|
||||
history: VecDeque<(Instant, PacketStatistics)>,
|
||||
|
||||
// Keep previous rates so that we can detect notable events
|
||||
rates: VecDeque<(Instant, PacketRates)>,
|
||||
}
|
||||
|
||||
impl PacketStatisticsControl {
|
||||
pub(crate) fn new() -> (Self, PacketStatisticsReporter) {
|
||||
let (stats_tx, stats_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
(
|
||||
Self {
|
||||
stats_rx,
|
||||
stats: PacketStatistics::default(),
|
||||
history: VecDeque::new(),
|
||||
rates: VecDeque::new(),
|
||||
},
|
||||
PacketStatisticsReporter::new(stats_tx),
|
||||
)
|
||||
}
|
||||
|
||||
// Add the current stats to the history, and remove old ones.
|
||||
fn update_history(&mut self) {
|
||||
// Update latest
|
||||
self.history.push_back((Instant::now(), self.stats.clone()));
|
||||
|
||||
// Filter out old ones
|
||||
let recording_window = Instant::now() - Duration::from_millis(RECORDING_WINDOW_MS);
|
||||
while self
|
||||
.history
|
||||
.front()
|
||||
.map_or(false, |&(t, _)| t < recording_window)
|
||||
{
|
||||
self.history.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_rates(&self) -> Option<PacketRates> {
|
||||
// NOTE: consider changing this to compute rates over the history instead of using current
|
||||
// stats. Currently it should not make much of a difference since we call this just after
|
||||
// updating the history, but it seems like it could be more internally consistent to do it
|
||||
// that way.
|
||||
|
||||
// Do basic averaging over the entire history, which just uses the first and last
|
||||
if let Some((start, start_stats)) = self.history.front() {
|
||||
let duration_secs = Instant::now().duration_since(*start).as_secs_f64();
|
||||
let delta = self.stats.clone() - start_stats.clone();
|
||||
let rates = PacketRates::from(delta) / duration_secs;
|
||||
Some(rates)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn update_rates(&mut self) {
|
||||
// Update latest
|
||||
if let Some(rates) = self.compute_rates() {
|
||||
self.rates.push_back((Instant::now(), rates));
|
||||
}
|
||||
|
||||
// Filter out old ones
|
||||
let recording_window = Instant::now() - Duration::from_millis(RECORDING_WINDOW_MS);
|
||||
while self
|
||||
.rates
|
||||
.front()
|
||||
.map_or(false, |&(t, _)| t < recording_window)
|
||||
{
|
||||
self.rates.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
fn report_rates(&self) {
|
||||
if let Some((_, rates)) = self.rates.back() {
|
||||
log::info!("{}", rates.summary());
|
||||
log::debug!("{}", rates.detailed_summary());
|
||||
}
|
||||
}
|
||||
|
||||
fn report_counters(&self) {
|
||||
log::trace!("packet statistics: {:?}", &self.stats);
|
||||
let (summary_sent, summary_recv) = self.stats.summary();
|
||||
log::debug!("{}", summary_sent);
|
||||
log::debug!("{}", summary_recv);
|
||||
}
|
||||
|
||||
fn check_for_notable_events(&self) {
|
||||
let Some((_, latest_rates)) = self.rates.back() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// If we get a burst of retransmissions
|
||||
// TODO: consider making this the number of retransmissions since last report instead.
|
||||
if latest_rates.retransmissions_queued > 0.0 {
|
||||
log::debug!(
|
||||
"retransmissions: {:.2} pkt/s",
|
||||
latest_rates.retransmissions_queued
|
||||
);
|
||||
|
||||
// Check what the number of retransmissions was during the recording window
|
||||
if let Some((_, start_stats)) = self.history.front() {
|
||||
let delta = self.stats.clone() - start_stats.clone();
|
||||
log::info!(
|
||||
"mix packet retransmissions/real mix packets: {}/{}",
|
||||
delta.retransmissions_queued,
|
||||
delta.real_packets_queued,
|
||||
);
|
||||
} else {
|
||||
log::warn!("Unable to check retransmissions during recording window");
|
||||
}
|
||||
}
|
||||
|
||||
// IDEA: if there is a burst of acks, that could indicate tokio task starvation.
|
||||
}
|
||||
|
||||
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
|
||||
log::debug!("Started PacketStatisticsControl with graceful shutdown support");
|
||||
|
||||
let report_interval = Duration::from_secs(PACKET_REPORT_INTERVAL_SECS);
|
||||
let mut report_interval = tokio::time::interval(report_interval);
|
||||
let snapshot_interval = Duration::from_millis(SNAPSHOT_INTERVAL_MS);
|
||||
let mut snapshot_interval = tokio::time::interval(snapshot_interval);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
stats_event = self.stats_rx.recv() => match stats_event {
|
||||
Some(stats_event) => {
|
||||
log::trace!("PacketStatisticsControl: Received stats event");
|
||||
self.stats.handle_event(stats_event);
|
||||
},
|
||||
None => {
|
||||
log::trace!("PacketStatisticsControl: stopping since stats channel was closed");
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = snapshot_interval.tick() => {
|
||||
self.update_history();
|
||||
self.update_rates();
|
||||
}
|
||||
_ = report_interval.tick() => {
|
||||
self.report_rates();
|
||||
self.check_for_notable_events();
|
||||
self.report_counters();
|
||||
}
|
||||
_ = shutdown.recv_with_delay() => {
|
||||
log::trace!("PacketStatisticsControl: Received shutdown");
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
log::debug!("PacketStatisticsControl: Exiting");
|
||||
}
|
||||
|
||||
pub(crate) fn start_with_shutdown(mut self, task_client: nym_task::TaskClient) {
|
||||
spawn_future(async move {
|
||||
self.run_with_shutdown(task_client).await;
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
|
||||
|
||||
use super::action_controller::{AckActionSender, Action};
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
@@ -19,7 +17,6 @@ pub(super) struct AcknowledgementListener {
|
||||
ack_key: Arc<AckKey>,
|
||||
ack_receiver: AcknowledgementReceiver,
|
||||
action_sender: AckActionSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
impl AcknowledgementListener {
|
||||
@@ -27,21 +24,16 @@ impl AcknowledgementListener {
|
||||
ack_key: Arc<AckKey>,
|
||||
ack_receiver: AcknowledgementReceiver,
|
||||
action_sender: AckActionSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
AcknowledgementListener {
|
||||
ack_key,
|
||||
ack_receiver,
|
||||
action_sender,
|
||||
stats_tx,
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_ack(&mut self, ack_content: Vec<u8>) {
|
||||
trace!("Received an ack");
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::AckReceived(ack_content.len()));
|
||||
|
||||
let frag_id = match recover_identifier(&self.ack_key, &ack_content)
|
||||
.map(FragmentIdentifier::try_from_bytes)
|
||||
{
|
||||
@@ -56,14 +48,11 @@ impl AcknowledgementListener {
|
||||
// because nothing was inserted in the first place
|
||||
if frag_id == COVER_FRAG_ID {
|
||||
trace!("Received an ack for a cover message - no need to do anything");
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::CoverAckReceived(ack_content.len()));
|
||||
return;
|
||||
}
|
||||
|
||||
trace!("Received {} from the mix network", frag_id);
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()));
|
||||
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_remove(frag_id))
|
||||
.unwrap();
|
||||
|
||||
@@ -8,7 +8,6 @@ use self::{
|
||||
sent_notification_listener::SentNotificationListener,
|
||||
};
|
||||
use crate::client::inbound_messages::InputMessageReceiver;
|
||||
use crate::client::packet_statistics_control::PacketStatisticsReporter;
|
||||
use crate::client::real_messages_control::message_handler::MessageHandler;
|
||||
use crate::client::replies::reply_controller::ReplyControllerSender;
|
||||
use crate::spawn_future;
|
||||
@@ -209,7 +208,6 @@ where
|
||||
connectors: AcknowledgementControllerConnectors,
|
||||
message_handler: MessageHandler<R>,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let (retransmission_tx, retransmission_rx) = mpsc::unbounded();
|
||||
|
||||
@@ -226,7 +224,6 @@ where
|
||||
Arc::clone(&ack_key),
|
||||
connectors.ack_receiver,
|
||||
connectors.ack_action_sender.clone(),
|
||||
stats_tx,
|
||||
);
|
||||
|
||||
// will listen for any new messages from the client
|
||||
|
||||
@@ -35,8 +35,6 @@ use crate::client::replies::reply_controller;
|
||||
use crate::config;
|
||||
pub(crate) use acknowledgement_control::{AckActionSender, Action};
|
||||
|
||||
use super::packet_statistics_control::PacketStatisticsReporter;
|
||||
|
||||
pub(crate) mod acknowledgement_control;
|
||||
pub(crate) mod message_handler;
|
||||
pub(crate) mod real_traffic_stream;
|
||||
@@ -145,7 +143,6 @@ impl RealMessagesController<OsRng> {
|
||||
reply_controller_receiver: ReplyControllerReceiver,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
client_connection_rx: ConnectionCommandReceiver,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let rng = OsRng;
|
||||
|
||||
@@ -184,7 +181,6 @@ impl RealMessagesController<OsRng> {
|
||||
ack_controller_connectors,
|
||||
message_handler.clone(),
|
||||
reply_controller_sender,
|
||||
stats_tx.clone(),
|
||||
);
|
||||
|
||||
let reply_control = ReplyController::new(
|
||||
@@ -203,7 +199,6 @@ impl RealMessagesController<OsRng> {
|
||||
topology_access,
|
||||
lane_queue_lengths,
|
||||
client_connection_rx,
|
||||
stats_tx,
|
||||
);
|
||||
|
||||
RealMessagesController {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use self::sending_delay_controller::SendingDelayController;
|
||||
use crate::client::mix_traffic::BatchMixMessageSender;
|
||||
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
|
||||
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
|
||||
use crate::client::topology_control::TopologyAccessor;
|
||||
use crate::client::transmission_buffer::TransmissionBuffer;
|
||||
@@ -114,9 +113,6 @@ where
|
||||
|
||||
/// Report queue lengths so that upstream can backoff sending data, and keep connections open.
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
|
||||
/// Channel used for sending statistics events to `PacketStatisticsControl`.
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -175,7 +171,6 @@ where
|
||||
topology_access: TopologyAccessor,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
client_connection_rx: ConnectionCommandReceiver,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
OutQueueControl {
|
||||
config,
|
||||
@@ -189,7 +184,6 @@ where
|
||||
transmission_buffer: TransmissionBuffer::new(),
|
||||
client_connection_rx,
|
||||
lane_queue_lengths,
|
||||
stats_tx,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +214,7 @@ where
|
||||
async fn on_message(&mut self, next_message: StreamMessage) {
|
||||
trace!("created new message");
|
||||
|
||||
let (next_message, fragment_id, packet_size) = match next_message {
|
||||
let (next_message, fragment_id) = match next_message {
|
||||
StreamMessage::Cover => {
|
||||
let cover_traffic_packet_size = self.loop_cover_message_size();
|
||||
trace!("the next loop cover message will be put in a {cover_traffic_packet_size} packet");
|
||||
@@ -256,28 +250,15 @@ where
|
||||
"Somehow failed to generate a loop cover message with a valid topology",
|
||||
),
|
||||
None,
|
||||
cover_traffic_packet_size.size(),
|
||||
)
|
||||
}
|
||||
StreamMessage::Real(real_message) => {
|
||||
let packet_size = real_message.packet_size();
|
||||
(
|
||||
real_message.mix_packet,
|
||||
real_message.fragment_id,
|
||||
packet_size,
|
||||
)
|
||||
(real_message.mix_packet, real_message.fragment_id)
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = self.mix_tx.send(vec![next_message]).await {
|
||||
log::error!("Failed to send: {err}");
|
||||
} else {
|
||||
let event = if fragment_id.is_some() {
|
||||
PacketStatisticsEvent::RealPacketSent(packet_size)
|
||||
} else {
|
||||
PacketStatisticsEvent::CoverPacketSent(packet_size)
|
||||
};
|
||||
self.stats_tx.report(event);
|
||||
}
|
||||
|
||||
// notify ack controller about sending our message only after we actually managed to push it
|
||||
@@ -359,28 +340,6 @@ where
|
||||
let lane_length = self.transmission_buffer.lane_length(&lane);
|
||||
self.lane_queue_lengths.set(&lane, lane_length);
|
||||
|
||||
// This is the last step in the pipeline where we know the type of the message, so
|
||||
// lets count the number of retransmissions and reply surb messages sent here.
|
||||
let stat_event = match lane {
|
||||
TransmissionLane::General => None,
|
||||
TransmissionLane::ConnectionId(_) => None,
|
||||
TransmissionLane::ReplySurbRequest => {
|
||||
Some(PacketStatisticsEvent::ReplySurbRequestQueued)
|
||||
}
|
||||
TransmissionLane::AdditionalReplySurbs => {
|
||||
Some(PacketStatisticsEvent::AdditionalReplySurbRequestQueued)
|
||||
}
|
||||
TransmissionLane::Retransmission => Some(PacketStatisticsEvent::RetransmissionQueued),
|
||||
};
|
||||
if let Some(stat_event) = stat_event {
|
||||
self.stats_tx.report(stat_event);
|
||||
}
|
||||
// To avoid comparing apples to oranges when presenting the fraction of packets that are
|
||||
// retransmissions, we also need to keep track to the total number of real messages queued,
|
||||
// even though we also track the actual number of messages sent later in the pipeline.
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RealPacketQueued);
|
||||
|
||||
Some(real_next)
|
||||
}
|
||||
|
||||
@@ -474,13 +433,6 @@ where
|
||||
Poll::Ready(Some((real_messages, conn_id))) => {
|
||||
log::trace!("handling real_messages: size: {}", real_messages.len());
|
||||
|
||||
// This is the last step in the pipeline where we know the type of the message, so
|
||||
// lets count the number of retransmissions here.
|
||||
if conn_id == TransmissionLane::Retransmission {
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RetransmissionQueued);
|
||||
}
|
||||
|
||||
// First store what we got for the given connection id
|
||||
self.transmission_buffer.store(&conn_id, real_messages);
|
||||
let real_next = self.pop_next_message().expect("we just added one");
|
||||
@@ -519,10 +471,10 @@ where
|
||||
let mult = self.sending_delay_controller.current_multiplier();
|
||||
let delay = self.current_average_message_sending_delay().as_millis();
|
||||
let status_str = if self.config.traffic.disable_main_poisson_packet_distribution {
|
||||
format!("Packet backlog: {backlog:.2} kiB ({packets}), {lanes} lanes, no delay")
|
||||
format!("Status: {lanes} lanes, backlog: {backlog:.2} kiB ({packets}), no delay")
|
||||
} else {
|
||||
format!(
|
||||
"Packet backlog: {backlog:.2} kiB ({packets}), {lanes} lanes, avg delay: {delay}ms ({mult})"
|
||||
"Status: {lanes} lanes, backlog: {backlog:.2} kiB ({packets}), avg delay: {delay}ms ({mult})"
|
||||
)
|
||||
};
|
||||
if packets > 1000 {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::{
|
||||
packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter},
|
||||
replies::{reply_controller::ReplyControllerSender, reply_storage::SentReplyKeys},
|
||||
};
|
||||
use crate::client::replies::reply_controller::ReplyControllerSender;
|
||||
use crate::client::replies::reply_storage::SentReplyKeys;
|
||||
use crate::spawn_future;
|
||||
use futures::channel::mpsc;
|
||||
use futures::lock::Mutex;
|
||||
@@ -45,33 +43,15 @@ struct ReceivedMessagesBufferInner<R: MessageReceiver> {
|
||||
// but perhaps it should be changed to include timestamps of when the message was reconstructed
|
||||
// and every now and then remove ids older than X
|
||||
recently_reconstructed: HashSet<i32>,
|
||||
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
}
|
||||
|
||||
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
fn recover_from_fragment(
|
||||
&mut self,
|
||||
fragment_data: &[u8],
|
||||
fragment_data_size: usize,
|
||||
) -> Option<NymMessage> {
|
||||
fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option<NymMessage> {
|
||||
if nym_sphinx::cover::is_cover(fragment_data) {
|
||||
trace!("The message was a loop cover message! Skipping it");
|
||||
// NOTE: it's important to note that there is quite a bit of difference in size of
|
||||
// received and sent packets due to the sphinx layers being removed by the exit gateway
|
||||
// before it reaches the mixnet client.
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::CoverPacketReceived(
|
||||
fragment_data_size,
|
||||
));
|
||||
return None;
|
||||
}
|
||||
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RealPacketReceived(
|
||||
fragment_data_size,
|
||||
));
|
||||
|
||||
let fragment = match self.message_receiver.recover_fragment(fragment_data) {
|
||||
Err(err) => {
|
||||
warn!("failed to recover fragment from raw data: {err}. The whole underlying message might be corrupted and unrecoverable!");
|
||||
@@ -123,17 +103,15 @@ impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
reply_ciphertext: &mut [u8],
|
||||
reply_key: SurbEncryptionKey,
|
||||
) -> Result<Option<NymMessage>, MessageRecoveryError> {
|
||||
let reply_ciphertext_size = reply_ciphertext.len();
|
||||
// note: this performs decryption IN PLACE without extra allocation
|
||||
self.message_receiver
|
||||
.recover_plaintext_from_reply(reply_ciphertext, reply_key)?;
|
||||
let fragment_data = reply_ciphertext;
|
||||
|
||||
Ok(self.recover_from_fragment(fragment_data, reply_ciphertext_size))
|
||||
Ok(self.recover_from_fragment(fragment_data))
|
||||
}
|
||||
|
||||
fn process_received_regular_packet(&mut self, mut raw_fragment: Vec<u8>) -> Option<NymMessage> {
|
||||
let raw_fragment_size = raw_fragment.len();
|
||||
let fragment_data = match self.message_receiver.recover_plaintext_from_regular_packet(
|
||||
self.local_encryption_keypair.private_key(),
|
||||
&mut raw_fragment,
|
||||
@@ -145,7 +123,7 @@ impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
Ok(frag_data) => frag_data,
|
||||
};
|
||||
|
||||
self.recover_from_fragment(fragment_data, raw_fragment_size)
|
||||
self.recover_from_fragment(fragment_data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +141,6 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
reply_key_storage: SentReplyKeys,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
stats_tx: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
ReceivedMessagesBuffer {
|
||||
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
|
||||
@@ -172,7 +149,6 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||
message_receiver: R::new(),
|
||||
message_sender: None,
|
||||
recently_reconstructed: HashSet::new(),
|
||||
stats_tx,
|
||||
})),
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
@@ -377,7 +353,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||
};
|
||||
|
||||
if let Some(completed) = completed_message {
|
||||
debug!("received {completed}");
|
||||
info!("received {completed}");
|
||||
completed_messages.push(completed)
|
||||
}
|
||||
}
|
||||
@@ -504,13 +480,11 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
|
||||
mixnet_packet_receiver: MixnetMessageReceiver,
|
||||
reply_key_storage: SentReplyKeys,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
packet_statistics_reporter: PacketStatisticsReporter,
|
||||
) -> Self {
|
||||
let received_buffer = ReceivedMessagesBuffer::new(
|
||||
local_encryption_keypair,
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
packet_statistics_reporter,
|
||||
);
|
||||
|
||||
ReceivedMessagesBufferController {
|
||||
|
||||
@@ -65,7 +65,7 @@ pub async fn current_gateways<R: Rng>(
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
|
||||
|
||||
log::debug!("Fetching list of gateways from: {nym_api}");
|
||||
log::trace!("Fetching list of gateways from: {nym_api}");
|
||||
|
||||
let gateways = client.get_cached_described_gateways().await?;
|
||||
log::debug!("Found {} gateways", gateways.len());
|
||||
|
||||
@@ -178,7 +178,7 @@ impl<T> From<PersistedGatewayDetails<T>> for GatewayDetails<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub enum GatewaySelectionSpecification<T = EmptyCustomDetails> {
|
||||
/// Uniformly choose a random remote gateway.
|
||||
UniformRemote { must_use_tls: bool },
|
||||
|
||||
@@ -26,8 +26,6 @@ use nym_task::TaskClient;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use rand::rngs::OsRng;
|
||||
use std::convert::TryFrom;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::fd::RawFd;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tungstenite::protocol::Message;
|
||||
@@ -36,7 +34,6 @@ use tungstenite::protocol::Message;
|
||||
use tokio::time::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
use tokio_tungstenite::MaybeTlsStream;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_utils::websocket::JSWebsocket;
|
||||
@@ -149,21 +146,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.gateway_identity
|
||||
}
|
||||
|
||||
pub fn ws_fd(&self) -> Option<RawFd> {
|
||||
match &self.connection {
|
||||
SocketState::Available(conn) => match conn.get_ref() {
|
||||
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
|
||||
MaybeTlsStream::NativeTls(stream) => Some(stream.as_raw_fd()),
|
||||
&_ => None,
|
||||
},
|
||||
SocketState::PartiallyDelegated(conn) => Some(conn.ws_fd()),
|
||||
_ => {
|
||||
log::warn!("No fd yet");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remaining_bandwidth(&self) -> i64 {
|
||||
self.bandwidth_remaining
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use futures::{SinkExt, StreamExt};
|
||||
use log::*;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_task::TaskClient;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
use tungstenite::Message;
|
||||
|
||||
@@ -39,15 +38,11 @@ type WsConn = JSWebsocket;
|
||||
type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>;
|
||||
|
||||
pub(crate) struct PartiallyDelegated {
|
||||
ws_fd: RawFd,
|
||||
sink_half: SplitSink<WsConn, Message>,
|
||||
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
|
||||
}
|
||||
|
||||
impl PartiallyDelegated {
|
||||
pub fn ws_fd(&self) -> RawFd {
|
||||
self.ws_fd
|
||||
}
|
||||
fn recover_received_plaintexts(ws_msgs: Vec<Message>, shared_key: &SharedKeys) -> Vec<Vec<u8>> {
|
||||
let mut plaintexts = Vec::with_capacity(ws_msgs.len());
|
||||
for ws_msg in ws_msgs {
|
||||
@@ -97,11 +92,6 @@ impl PartiallyDelegated {
|
||||
let (notify_sender, notify_receiver) = oneshot::channel();
|
||||
let (stream_sender, stream_receiver) = oneshot::channel();
|
||||
|
||||
let ws_fd = match conn.get_ref() {
|
||||
MaybeTlsStream::Plain(stream) => stream.as_raw_fd(),
|
||||
MaybeTlsStream::NativeTls(stream) => stream.as_raw_fd(),
|
||||
_ => 0.into(),
|
||||
};
|
||||
let (sink, mut stream) = conn.split();
|
||||
|
||||
let mixnet_receiver_future = async move {
|
||||
@@ -151,7 +141,6 @@ impl PartiallyDelegated {
|
||||
tokio::spawn(mixnet_receiver_future);
|
||||
|
||||
PartiallyDelegated {
|
||||
ws_fd,
|
||||
sink_half: sink,
|
||||
delegated_stream: (stream_receiver, notify_sender),
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::signing::tx_signer::TxSigner;
|
||||
use crate::signing::AccountData;
|
||||
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::cosmwasm;
|
||||
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
|
||||
use cosmrs::tx::{Raw, SignDoc};
|
||||
use cosmwasm_std::Addr;
|
||||
@@ -39,7 +40,7 @@ pub use crate::rpc::TendermintRpcClient;
|
||||
pub use coin::Coin;
|
||||
pub use cosmrs::{
|
||||
bank::MsgSend,
|
||||
bip32, cosmwasm,
|
||||
bip32,
|
||||
crypto::PublicKey,
|
||||
query::{PageRequest, PageResponse},
|
||||
tendermint::{
|
||||
|
||||
@@ -13,11 +13,9 @@ bs58 = "0.4"
|
||||
comfy-table = "6.0.0"
|
||||
cfg-if = "1.0.0"
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
csv = "1.3.0"
|
||||
cw-utils = { workspace = true }
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
inquire = "0.6.2"
|
||||
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
|
||||
log = { workspace = true }
|
||||
rand = {version = "0.6", features = ["std"] }
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
n1q85lscptz860j3dx92f8phaeaw08j2l5dt7adq,50,nym
|
||||
n136ckky0n39eskewg04xhvahq9yp9f7sgtnxytt,50,unym
|
||||
n1xh7ru0zp67czxhvx0r5e8ur7rvg6n3ymmje0ju,50,nyx
|
||||
n13mpm4aj03alffnvz2x9ynjy4u3cxemxn2xw34w,50,unyx
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
@@ -7,7 +7,6 @@ pub mod balance;
|
||||
pub mod create;
|
||||
pub mod pubkey;
|
||||
pub mod send;
|
||||
pub mod send_multiple;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
@@ -26,6 +25,4 @@ pub enum AccountCommands {
|
||||
PubKey(crate::validator::account::pubkey::Args),
|
||||
/// Sends tokens to another account
|
||||
Send(crate::validator::account::send::Args),
|
||||
/// Batch multiple token sends
|
||||
SendMultiple(crate::validator::account::send_multiple::Args),
|
||||
}
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::pretty_coin;
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use cosmrs::rpc::endpoint::tx::Response;
|
||||
use log::{error, info};
|
||||
use nym_validator_client::nyxd::{AccountId, Coin};
|
||||
use serde_json::json;
|
||||
use std::str::FromStr;
|
||||
use std::{fs, io::Write};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub memo: Option<String>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "Input file path (CSV format) with account/amount pairs to send"
|
||||
)]
|
||||
pub input: String,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "An output file path (CSV format) to create or append a log of results to"
|
||||
)]
|
||||
pub output: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn send_multiple(args: Args, client: &SigningClient) {
|
||||
let memo = args
|
||||
.memo
|
||||
.unwrap_or_else(|| "Sending tokens with nym-cli".to_owned());
|
||||
|
||||
let rows = InputFileReader::new(&args.input);
|
||||
if let Err(e) = rows {
|
||||
error!("Failed to read input file: {}", e);
|
||||
return;
|
||||
}
|
||||
let rows = rows.unwrap();
|
||||
|
||||
let mut table = Table::new();
|
||||
|
||||
if rows.rows.is_empty() {
|
||||
error!("No transactions to send");
|
||||
return;
|
||||
}
|
||||
|
||||
println!(
|
||||
"The following transfer will be made from account {} to:",
|
||||
client.address()
|
||||
);
|
||||
table.set_header(vec!["Address", "Amount"]);
|
||||
|
||||
for row in rows.rows.iter() {
|
||||
table.add_row(vec![row.address.to_string(), pretty_coin(&row.amount)]);
|
||||
}
|
||||
|
||||
println!("{table}");
|
||||
|
||||
let ans = inquire::Confirm::new("Do you want to continue with the transfers?")
|
||||
.with_default(false)
|
||||
.with_help_message("You must confirm before the transaction will be sent")
|
||||
.prompt();
|
||||
|
||||
if let Err(e) = ans {
|
||||
info!("Aborting, {}...", e);
|
||||
return;
|
||||
}
|
||||
if let Ok(false) = ans {
|
||||
info!("Aborting!");
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Transferring from {}...", client.address());
|
||||
|
||||
let multiple_sends: Vec<(AccountId, Vec<Coin>)> = rows
|
||||
.rows
|
||||
.iter()
|
||||
.map(|row| (row.address.clone(), vec![row.amount.clone()]))
|
||||
.collect();
|
||||
|
||||
let res = client
|
||||
.send_multiple(multiple_sends, memo, None)
|
||||
.await
|
||||
.expect("failed to send tokens!");
|
||||
|
||||
info!("Sending result: {}", json!(res));
|
||||
|
||||
println!();
|
||||
println!(
|
||||
"Nodesguru: https://nym.explorers.guru/transaction/{}",
|
||||
&res.hash
|
||||
);
|
||||
println!("Mintscan: https://www.mintscan.io/nyx/txs/{}", &res.hash);
|
||||
println!("Transaction result code: {}", &res.tx_result.code.value());
|
||||
println!("Transaction hash: {}", &res.hash);
|
||||
|
||||
if let Some(output_filename) = args.output {
|
||||
println!("\nWriting output log to {}", output_filename);
|
||||
|
||||
if let Err(e) = write_output_file(rows, res, &output_filename) {
|
||||
error!(
|
||||
"Failed to write output file {} with error {}",
|
||||
output_filename, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_output_file(
|
||||
rows: InputFileReader,
|
||||
res: Response,
|
||||
output_filename: &String,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(output_filename)?;
|
||||
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
let now = now.format(&time::format_description::well_known::Rfc3339)?;
|
||||
|
||||
let data = rows
|
||||
.rows
|
||||
.iter()
|
||||
.map(|row| {
|
||||
format!(
|
||||
"{},{},{},{},{}",
|
||||
row.address, row.amount.amount, row.amount.denom, now, res.hash
|
||||
)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
Ok(file.write_all(format!("{}\n", data).as_bytes())?)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InputFileRow {
|
||||
pub address: AccountId,
|
||||
pub amount: Coin,
|
||||
}
|
||||
|
||||
pub struct InputFileReader {
|
||||
pub rows: Vec<InputFileRow>,
|
||||
}
|
||||
|
||||
impl InputFileReader {
|
||||
pub fn new(path: &str) -> Result<InputFileReader, anyhow::Error> {
|
||||
let mut rows: Vec<InputFileRow> = vec![];
|
||||
let file_contents = fs::read_to_string(path)?;
|
||||
|
||||
let lines: Vec<String> = file_contents.lines().map(String::from).collect();
|
||||
for line in lines {
|
||||
let tokens: Vec<_> = line.split(',').collect();
|
||||
if tokens.len() < 3 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"'{}' does not have enough columns, expecting <address>,<amount>,<denom>",
|
||||
line
|
||||
));
|
||||
}
|
||||
// try parse amount to u128
|
||||
let amount = u128::from_str(tokens[1])
|
||||
.map_err(|_| anyhow::anyhow!("'{}' has an invalid amount", line))?;
|
||||
|
||||
let denom: String = tokens[2].into();
|
||||
|
||||
// multiply when a whole token amount, e.g. 50nym (50.123456nym is not allowed, that must be input as 50123456unym)
|
||||
let (amount, denom) = if !denom.starts_with('u') {
|
||||
(amount * 1_000_000u128, format!("u{}", denom))
|
||||
} else {
|
||||
(amount, denom)
|
||||
};
|
||||
|
||||
let address = AccountId::from_str(tokens[0])
|
||||
.map_err(|e| anyhow::anyhow!("'{}' has an invalid address: {}", line, e))?;
|
||||
|
||||
let amount = Coin { amount, denom };
|
||||
|
||||
rows.push(InputFileRow { address, amount })
|
||||
}
|
||||
|
||||
Ok(InputFileReader { rows })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_multiple_send_input_csv {
|
||||
use super::*;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use std::str::FromStr;
|
||||
#[test]
|
||||
fn works_on_happy_path() {
|
||||
let input_csv = InputFileReader::new("fixtures/test_send_multiple.csv").unwrap();
|
||||
assert_eq!(
|
||||
AccountId::from_str("n1q85lscptz860j3dx92f8phaeaw08j2l5dt7adq").unwrap(),
|
||||
input_csv.rows[0].address
|
||||
);
|
||||
|
||||
println!("{:?}", input_csv.rows);
|
||||
|
||||
assert_eq!(50_000_000u128, input_csv.rows[0].amount.amount);
|
||||
assert_eq!(50u128, input_csv.rows[1].amount.amount);
|
||||
assert_eq!(50_000_000u128, input_csv.rows[2].amount.amount);
|
||||
assert_eq!(50u128, input_csv.rows[3].amount.amount);
|
||||
|
||||
assert_eq!("unym", input_csv.rows[0].amount.denom);
|
||||
assert_eq!("unym", input_csv.rows[1].amount.denom);
|
||||
assert_eq!("unyx", input_csv.rows[2].amount.denom);
|
||||
assert_eq!("unyx", input_csv.rows[3].amount.denom);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
use clap::Parser;
|
||||
use log::{debug, info};
|
||||
use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS;
|
||||
use std::str::FromStr;
|
||||
|
||||
use nym_coconut_dkg_common::msg::InstantiateMsg;
|
||||
use nym_coconut_dkg_common::types::TimeConfiguration;
|
||||
use nym_coconut_dkg_common::types::{TimeConfiguration, DEFAULT_DEALINGS};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
||||
@@ -40,12 +40,12 @@ pub fn chunk_dealing(
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct DealingChunkInfo {
|
||||
pub size: u64,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl DealingChunkInfo {
|
||||
pub fn new(size: usize) -> Self {
|
||||
DealingChunkInfo { size: size as u64 }
|
||||
DealingChunkInfo { size }
|
||||
}
|
||||
|
||||
pub fn construct(dealing_len: usize, chunk_size: usize) -> Vec<Self> {
|
||||
@@ -128,10 +128,7 @@ impl DealingMetadata {
|
||||
}
|
||||
|
||||
pub fn total_size(&self) -> usize {
|
||||
self.submitted_chunks
|
||||
.values()
|
||||
.map(|c| c.info.size as usize)
|
||||
.sum()
|
||||
self.submitted_chunks.values().map(|c| c.info.size).sum()
|
||||
}
|
||||
|
||||
pub fn submission_statuses(&self) -> BTreeMap<ChunkIndex, ChunkSubmissionStatus> {
|
||||
@@ -275,16 +272,13 @@ mod tests {
|
||||
for (dealing_len, chunk_size, expected_chunks) in test_cases {
|
||||
let chunks = DealingChunkInfo::construct(dealing_len, chunk_size);
|
||||
assert_eq!(expected_chunks, chunks.len());
|
||||
assert_eq!(
|
||||
dealing_len as u64,
|
||||
chunks.iter().map(|c| c.size).sum::<u64>()
|
||||
);
|
||||
assert_eq!(dealing_len, chunks.iter().map(|c| c.size).sum::<usize>());
|
||||
|
||||
let mut expected_last = dealing_len % chunk_size;
|
||||
if expected_last == 0 {
|
||||
expected_last = chunk_size;
|
||||
}
|
||||
assert_eq!(chunks.last().unwrap().size, expected_last as u64);
|
||||
assert_eq!(chunks.last().unwrap().size, expected_last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,8 +252,4 @@ impl EpochState {
|
||||
pub fn is_final(&self) -> bool {
|
||||
*self == EpochState::InProgress
|
||||
}
|
||||
|
||||
pub fn is_in_progress(&self) -> bool {
|
||||
matches!(self, EpochState::InProgress)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
@@ -35,12 +35,6 @@ impl From<Vec<u8>> for ContractSafeBytes {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for ContractSafeBytes {
|
||||
fn from(value: &'a [u8]) -> Self {
|
||||
value.to_vec().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ContractSafeBytes {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if !self.0.is_empty() {
|
||||
|
||||
@@ -8,13 +8,12 @@ documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
bytes = "1.5.0"
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
rand = "0.8.5"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("{0}")]
|
||||
IO(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
pub const BUFFER_TIMEOUT: Duration = Duration::from_millis(20);
|
||||
|
||||
// TODO: increase this to make max out effective sphinx payload size. Sphinx packets also carry the
|
||||
// MixAck so that's why we can't just use 2kb.
|
||||
pub const MAX_PACKET_SIZE: usize = 1500;
|
||||
|
||||
// Each IP packet is prefixed by a 2 byte length prefix
|
||||
const LENGTH_PREFIX_SIZE: usize = 2;
|
||||
|
||||
// Tokio codec for bundling multiple IP packets into one buffer that is at most 1500 bytes long.
|
||||
// These packets are separated by a 2 byte length prefix. We need a timer so that we don't wait too
|
||||
// long for the buffer to fill up, since this kills latency.
|
||||
pub struct MultiIpPacketCodec {
|
||||
buffer: BytesMut,
|
||||
buffer_timeout: tokio::time::Interval,
|
||||
}
|
||||
|
||||
impl MultiIpPacketCodec {
|
||||
pub fn new(buffer_timeout: Duration) -> Self {
|
||||
MultiIpPacketCodec {
|
||||
buffer: BytesMut::new(),
|
||||
buffer_timeout: tokio::time::interval(buffer_timeout),
|
||||
}
|
||||
}
|
||||
|
||||
// Append a packet to the buffer and return the buffer if it's full
|
||||
pub fn append_packet(&mut self, packet: Bytes) -> Option<Bytes> {
|
||||
let mut bundled_packets = BytesMut::new();
|
||||
self.encode(packet, &mut bundled_packets).unwrap();
|
||||
if bundled_packets.is_empty() {
|
||||
None
|
||||
} else {
|
||||
// log::info!("Sphinx packet utilization: {:.2}", self.buffer.len() as f64 / MAX_PACKET_SIZE as f64);
|
||||
Some(bundled_packets.freeze())
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the current buffer and return it.
|
||||
fn flush_current_buffer(&mut self) -> Bytes {
|
||||
let mut output_buffer = BytesMut::new();
|
||||
std::mem::swap(&mut output_buffer, &mut self.buffer);
|
||||
output_buffer.freeze()
|
||||
}
|
||||
|
||||
// Wait for the buffer_timeout to tick and then flush the buffer.
|
||||
// This is useful when we want to send the buffer even if it's not full.
|
||||
pub async fn buffer_timeout(&mut self) -> Option<Bytes> {
|
||||
// Wait for buffer_timeout to tick
|
||||
let _ = self.buffer_timeout.tick().await;
|
||||
|
||||
// Flush the buffer and return it
|
||||
let packets = self.flush_current_buffer();
|
||||
if packets.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(packets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder<Bytes> for MultiIpPacketCodec {
|
||||
type Error = Error;
|
||||
|
||||
fn encode(&mut self, packet: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
if self.buffer.is_empty() {
|
||||
self.buffer_timeout.reset();
|
||||
}
|
||||
let packet_size = packet.len();
|
||||
|
||||
if self.buffer.len() + packet_size + LENGTH_PREFIX_SIZE > MAX_PACKET_SIZE {
|
||||
// If the packet doesn't fit in the buffer, send the buffer and then add it to the buffer
|
||||
dst.extend_from_slice(&self.buffer);
|
||||
self.buffer = BytesMut::new();
|
||||
self.buffer_timeout.reset();
|
||||
}
|
||||
|
||||
// Add the packet size
|
||||
self.buffer
|
||||
.extend_from_slice(&(packet_size as u16).to_be_bytes());
|
||||
// Add the packet to the buffer
|
||||
self.buffer.extend_from_slice(&packet);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for MultiIpPacketCodec {
|
||||
type Item = Bytes;
|
||||
type Error = Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if src.len() < LENGTH_PREFIX_SIZE {
|
||||
// Not enough bytes to read the length prefix
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let packet_size = u16::from_be_bytes([src[0], src[1]]) as usize;
|
||||
|
||||
if src.len() < packet_size + LENGTH_PREFIX_SIZE {
|
||||
// Not enough bytes to read the packet
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Remove the length prefix
|
||||
src.advance(LENGTH_PREFIX_SIZE);
|
||||
|
||||
// Read the packet
|
||||
let packet = src.split_to(packet_size);
|
||||
|
||||
Ok(Some(packet.freeze()))
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,319 @@
|
||||
pub mod codec;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
use std::net::IpAddr;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = 3;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const CURRENT_VERSION: u8 = 1;
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct IpPacketRequest {
|
||||
pub version: u8,
|
||||
pub data: IpPacketRequestData,
|
||||
}
|
||||
|
||||
impl IpPacketRequest {
|
||||
pub fn new_static_connect_request(
|
||||
ip: IpAddr,
|
||||
reply_to: Recipient,
|
||||
reply_to_hops: Option<u8>,
|
||||
reply_to_avg_mix_delays: Option<f64>,
|
||||
) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::StaticConnect(StaticConnectRequest {
|
||||
request_id,
|
||||
ip,
|
||||
reply_to,
|
||||
reply_to_hops,
|
||||
reply_to_avg_mix_delays,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_request(
|
||||
reply_to: Recipient,
|
||||
reply_to_hops: Option<u8>,
|
||||
reply_to_avg_mix_delays: Option<f64>,
|
||||
) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::DynamicConnect(DynamicConnectRequest {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply_to_hops,
|
||||
reply_to_avg_mix_delays,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::Data(DataRequest { ip_packet }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
IpPacketRequestData::StaticConnect(request) => Some(request.request_id),
|
||||
IpPacketRequestData::DynamicConnect(request) => Some(request.request_id),
|
||||
IpPacketRequestData::Data(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> Option<&Recipient> {
|
||||
match &self.data {
|
||||
IpPacketRequestData::StaticConnect(request) => Some(&request.reply_to),
|
||||
IpPacketRequestData::DynamicConnect(request) => Some(&request.reply_to),
|
||||
IpPacketRequestData::Data(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum IpPacketRequestData {
|
||||
StaticConnect(StaticConnectRequest),
|
||||
DynamicConnect(DynamicConnectRequest),
|
||||
Data(DataRequest),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct StaticConnectRequest {
|
||||
pub request_id: u64,
|
||||
pub ip: IpAddr,
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
// The number of mix node hops that responses should take, in addition to the entry and exit
|
||||
// node. Zero means only client -> entry -> exit -> client.
|
||||
pub reply_to_hops: Option<u8>,
|
||||
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
||||
// ip packet router.
|
||||
pub reply_to_avg_mix_delays: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DynamicConnectRequest {
|
||||
pub request_id: u64,
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
// The number of mix node hops that responses should take, in addition to the entry and exit
|
||||
// node. Zero means only client -> entry -> exit -> client.
|
||||
pub reply_to_hops: Option<u8>,
|
||||
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
||||
// ip packet router.
|
||||
pub reply_to_avg_mix_delays: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DataRequest {
|
||||
pub ip_packet: bytes::Bytes,
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct IpPacketResponse {
|
||||
pub version: u8,
|
||||
pub data: IpPacketResponseData,
|
||||
}
|
||||
|
||||
impl IpPacketResponse {
|
||||
pub fn new_static_connect_success(request_id: u64, reply_to: Recipient) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::StaticConnect(StaticConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: StaticConnectResponseReply::Success,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_static_connect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: StaticConnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::StaticConnect(StaticConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: StaticConnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ip: IpAddr) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ip }),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: DynamicConnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DynamicConnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Data(DataResponse { ip_packet }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
IpPacketResponseData::StaticConnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::DynamicConnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::Data(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> Option<&Recipient> {
|
||||
match &self.data {
|
||||
IpPacketResponseData::StaticConnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::DynamicConnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Data(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum IpPacketResponseData {
|
||||
StaticConnect(StaticConnectResponse),
|
||||
DynamicConnect(DynamicConnectResponse),
|
||||
Data(DataResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct StaticConnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: StaticConnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum StaticConnectResponseReply {
|
||||
Success,
|
||||
Failure(StaticConnectFailureReason),
|
||||
}
|
||||
|
||||
impl StaticConnectResponseReply {
|
||||
pub fn is_success(&self) -> bool {
|
||||
match self {
|
||||
StaticConnectResponseReply::Success => true,
|
||||
StaticConnectResponseReply::Failure(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum StaticConnectFailureReason {
|
||||
#[error("requested ip address is already in use")]
|
||||
RequestedIpAlreadyInUse,
|
||||
#[error("requested nym-address is already in use")]
|
||||
RequestedNymAddressAlreadyInUse,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DynamicConnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: DynamicConnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum DynamicConnectResponseReply {
|
||||
Success(DynamicConnectSuccess),
|
||||
Failure(DynamicConnectFailureReason),
|
||||
}
|
||||
|
||||
impl DynamicConnectResponseReply {
|
||||
pub fn is_success(&self) -> bool {
|
||||
match self {
|
||||
DynamicConnectResponseReply::Success(_) => true,
|
||||
DynamicConnectResponseReply::Failure(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DynamicConnectSuccess {
|
||||
pub ip: IpAddr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum DynamicConnectFailureReason {
|
||||
#[error("requested nym-address is already in use")]
|
||||
RequestedNymAddressAlreadyInUse,
|
||||
#[error("no available ip address")]
|
||||
NoAvailableIp,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DataResponse {
|
||||
pub ip_packet: bytes::Bytes,
|
||||
}
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
@@ -10,3 +321,63 @@ fn make_bincode_serializer() -> impl bincode::Options {
|
||||
.with_big_endian()
|
||||
.with_varint_encoding()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn check_size_of_request() {
|
||||
let connect = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::StaticConnect(
|
||||
StaticConnectRequest {
|
||||
request_id: 123,
|
||||
ip: IpAddr::from([10, 0, 0, 1]),
|
||||
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
|
||||
reply_to_hops: None,
|
||||
reply_to_avg_mix_delays: None,
|
||||
},
|
||||
)
|
||||
};
|
||||
assert_eq!(connect.to_bytes().unwrap().len(), 107);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_size_of_data() {
|
||||
let data = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::Data(DataRequest {
|
||||
ip_packet: bytes::Bytes::from(vec![1u8; 32]),
|
||||
}),
|
||||
};
|
||||
assert_eq!(data.to_bytes().unwrap().len(), 35);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_and_deserialize_data_request() {
|
||||
let data = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::Data(DataRequest {
|
||||
ip_packet: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
|
||||
}),
|
||||
};
|
||||
|
||||
let serialized = data.to_bytes().unwrap();
|
||||
let deserialized = IpPacketRequest::from_reconstructed_message(
|
||||
&nym_sphinx::receiver::ReconstructedMessage {
|
||||
message: serialized,
|
||||
sender_tag: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(deserialized.version, 4);
|
||||
assert_eq!(
|
||||
deserialized.data,
|
||||
IpPacketRequestData::Data(DataRequest {
|
||||
ip_packet: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, CURRENT_VERSION};
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct IpPacketRequest {
|
||||
pub version: u8,
|
||||
pub data: IpPacketRequestData,
|
||||
}
|
||||
|
||||
impl IpPacketRequest {
|
||||
pub fn new_static_connect_request(
|
||||
ip: IpAddr,
|
||||
reply_to: Recipient,
|
||||
reply_to_hops: Option<u8>,
|
||||
reply_to_avg_mix_delays: Option<f64>,
|
||||
buffer_timeout: Option<u64>,
|
||||
) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::StaticConnect(StaticConnectRequest {
|
||||
request_id,
|
||||
ip,
|
||||
reply_to,
|
||||
reply_to_hops,
|
||||
reply_to_avg_mix_delays,
|
||||
buffer_timeout,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_request(
|
||||
reply_to: Recipient,
|
||||
reply_to_hops: Option<u8>,
|
||||
reply_to_avg_mix_delays: Option<f64>,
|
||||
buffer_timeout: Option<u64>,
|
||||
) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::DynamicConnect(DynamicConnectRequest {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply_to_hops,
|
||||
reply_to_avg_mix_delays,
|
||||
buffer_timeout,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_disconnect_request(reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::Disconnect(DisconnectRequest {
|
||||
request_id,
|
||||
reply_to,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_data_request(ip_packets: bytes::Bytes) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::Data(DataRequest { ip_packets }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
IpPacketRequestData::StaticConnect(request) => Some(request.request_id),
|
||||
IpPacketRequestData::DynamicConnect(request) => Some(request.request_id),
|
||||
IpPacketRequestData::Disconnect(request) => Some(request.request_id),
|
||||
IpPacketRequestData::Data(_) => None,
|
||||
IpPacketRequestData::Ping(request) => Some(request.request_id),
|
||||
IpPacketRequestData::Health(request) => Some(request.request_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> Option<&Recipient> {
|
||||
match &self.data {
|
||||
IpPacketRequestData::StaticConnect(request) => Some(&request.reply_to),
|
||||
IpPacketRequestData::DynamicConnect(request) => Some(&request.reply_to),
|
||||
IpPacketRequestData::Disconnect(request) => Some(&request.reply_to),
|
||||
IpPacketRequestData::Data(_) => None,
|
||||
IpPacketRequestData::Ping(request) => Some(&request.reply_to),
|
||||
IpPacketRequestData::Health(request) => Some(&request.reply_to),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum IpPacketRequestData {
|
||||
StaticConnect(StaticConnectRequest),
|
||||
DynamicConnect(DynamicConnectRequest),
|
||||
Disconnect(DisconnectRequest),
|
||||
Data(DataRequest),
|
||||
Ping(PingRequest),
|
||||
Health(HealthRequest),
|
||||
}
|
||||
|
||||
// A static connect request is when the client provides the internal IP address it will use on the
|
||||
// ip packet router.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct StaticConnectRequest {
|
||||
pub request_id: u64,
|
||||
|
||||
pub ip: IpAddr,
|
||||
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
|
||||
// The number of mix node hops that responses should take, in addition to the entry and exit
|
||||
// node. Zero means only client -> entry -> exit -> client.
|
||||
pub reply_to_hops: Option<u8>,
|
||||
|
||||
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
||||
// ip packet router.
|
||||
pub reply_to_avg_mix_delays: Option<f64>,
|
||||
|
||||
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
|
||||
// with ip packets.
|
||||
pub buffer_timeout: Option<u64>,
|
||||
}
|
||||
|
||||
// A dynamic connect request is when the client does not provide the internal IP address it will use
|
||||
// on the ip packet router, and instead requests one to be assigned to it.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DynamicConnectRequest {
|
||||
pub request_id: u64,
|
||||
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
|
||||
// The number of mix node hops that responses should take, in addition to the entry and exit
|
||||
// node. Zero means only client -> entry -> exit -> client.
|
||||
pub reply_to_hops: Option<u8>,
|
||||
|
||||
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
||||
// ip packet router.
|
||||
pub reply_to_avg_mix_delays: Option<f64>,
|
||||
|
||||
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
|
||||
// with ip packets.
|
||||
pub buffer_timeout: Option<u64>,
|
||||
}
|
||||
|
||||
// A disconnect request is when the client wants to disconnect from the ip packet router and free
|
||||
// up the allocated IP address.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DisconnectRequest {
|
||||
pub request_id: u64,
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
}
|
||||
|
||||
// A data request is when the client wants to send an IP packet to a destination.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DataRequest {
|
||||
pub ip_packets: bytes::Bytes,
|
||||
}
|
||||
|
||||
// A ping request is when the client wants to check if the ip packet router is still alive.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PingRequest {
|
||||
pub request_id: u64,
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct HealthRequest {
|
||||
pub request_id: u64,
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn check_size_of_request() {
|
||||
let connect = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::StaticConnect(
|
||||
StaticConnectRequest {
|
||||
request_id: 123,
|
||||
ip: IpAddr::from([10, 0, 0, 1]),
|
||||
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
|
||||
reply_to_hops: None,
|
||||
reply_to_avg_mix_delays: None,
|
||||
buffer_timeout: None,
|
||||
},
|
||||
)
|
||||
};
|
||||
assert_eq!(connect.to_bytes().unwrap().len(), 108);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_size_of_data() {
|
||||
let data = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::Data(DataRequest {
|
||||
ip_packets: bytes::Bytes::from(vec![1u8; 32]),
|
||||
}),
|
||||
};
|
||||
assert_eq!(data.to_bytes().unwrap().len(), 35);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_and_deserialize_data_request() {
|
||||
let data = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::Data(DataRequest {
|
||||
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
|
||||
}),
|
||||
};
|
||||
|
||||
let serialized = data.to_bytes().unwrap();
|
||||
let deserialized = IpPacketRequest::from_reconstructed_message(
|
||||
&nym_sphinx::receiver::ReconstructedMessage {
|
||||
message: serialized,
|
||||
sender_tag: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(deserialized.version, 4);
|
||||
assert_eq!(
|
||||
deserialized.data,
|
||||
IpPacketRequestData::Data(DataRequest {
|
||||
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,362 +0,0 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, CURRENT_VERSION};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct IpPacketResponse {
|
||||
pub version: u8,
|
||||
pub data: IpPacketResponseData,
|
||||
}
|
||||
|
||||
impl IpPacketResponse {
|
||||
pub fn new_static_connect_success(request_id: u64, reply_to: Recipient) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::StaticConnect(StaticConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: StaticConnectResponseReply::Success,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_static_connect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: StaticConnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::StaticConnect(StaticConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: StaticConnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ip: IpAddr) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ip }),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: DynamicConnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DynamicConnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_disconnect_success(request_id: u64, reply_to: Recipient) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Disconnect(DisconnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DisconnectResponseReply::Success,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_disconnect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: DisconnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Disconnect(DisconnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DisconnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_unrequested_disconnect(
|
||||
reply_to: Recipient,
|
||||
reason: UnrequestedDisconnectReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::UnrequestedDisconnect(UnrequestedDisconnect {
|
||||
reply_to,
|
||||
reason,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Data(DataResponse { ip_packet }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_version_mismatch(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
request_version: u8,
|
||||
our_version: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Error(ErrorResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: ErrorResponseReply::VersionMismatch {
|
||||
request_version,
|
||||
response_version: our_version,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_data_error_response(reply_to: Recipient, reply: ErrorResponseReply) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Error(ErrorResponse {
|
||||
request_id: 0,
|
||||
reply_to,
|
||||
reply,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
IpPacketResponseData::StaticConnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::DynamicConnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::Disconnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::UnrequestedDisconnect(_) => None,
|
||||
IpPacketResponseData::Data(_) => None,
|
||||
IpPacketResponseData::Pong(response) => Some(response.request_id),
|
||||
IpPacketResponseData::Health(response) => Some(response.request_id),
|
||||
IpPacketResponseData::Error(response) => Some(response.request_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> Option<&Recipient> {
|
||||
match &self.data {
|
||||
IpPacketResponseData::StaticConnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::DynamicConnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Disconnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::UnrequestedDisconnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Data(_) => None,
|
||||
IpPacketResponseData::Pong(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Health(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Error(response) => Some(&response.reply_to),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum IpPacketResponseData {
|
||||
// Response for a static connect request
|
||||
StaticConnect(StaticConnectResponse),
|
||||
|
||||
// Response for a dynamic connect request
|
||||
DynamicConnect(DynamicConnectResponse),
|
||||
|
||||
// Response for a disconnect initiqated by the client
|
||||
Disconnect(DisconnectResponse),
|
||||
|
||||
// Message from the server that the client got disconnected without the client initiating it
|
||||
UnrequestedDisconnect(UnrequestedDisconnect),
|
||||
|
||||
// Response to a data request
|
||||
Data(DataResponse),
|
||||
|
||||
// Response to ping request
|
||||
Pong(PongResponse),
|
||||
|
||||
// Response for a health request
|
||||
Health(HealthResponse),
|
||||
|
||||
// Error response
|
||||
Error(ErrorResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct StaticConnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: StaticConnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum StaticConnectResponseReply {
|
||||
Success,
|
||||
Failure(StaticConnectFailureReason),
|
||||
}
|
||||
|
||||
impl StaticConnectResponseReply {
|
||||
pub fn is_success(&self) -> bool {
|
||||
match self {
|
||||
StaticConnectResponseReply::Success => true,
|
||||
StaticConnectResponseReply::Failure(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum StaticConnectFailureReason {
|
||||
#[error("requested ip address is already in use")]
|
||||
RequestedIpAlreadyInUse,
|
||||
#[error("requested nym-address is already in use")]
|
||||
RequestedNymAddressAlreadyInUse,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DynamicConnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: DynamicConnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum DynamicConnectResponseReply {
|
||||
Success(DynamicConnectSuccess),
|
||||
Failure(DynamicConnectFailureReason),
|
||||
}
|
||||
|
||||
impl DynamicConnectResponseReply {
|
||||
pub fn is_success(&self) -> bool {
|
||||
match self {
|
||||
DynamicConnectResponseReply::Success(_) => true,
|
||||
DynamicConnectResponseReply::Failure(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DynamicConnectSuccess {
|
||||
pub ip: IpAddr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum DynamicConnectFailureReason {
|
||||
#[error("requested nym-address is already in use")]
|
||||
RequestedNymAddressAlreadyInUse,
|
||||
#[error("no available ip address")]
|
||||
NoAvailableIp,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DisconnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: DisconnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum DisconnectResponseReply {
|
||||
Success,
|
||||
Failure(DisconnectFailureReason),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum DisconnectFailureReason {
|
||||
#[error("requested nym-address is not currently connected")]
|
||||
RequestedNymAddressNotConnected,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct UnrequestedDisconnect {
|
||||
pub reply_to: Recipient,
|
||||
pub reason: UnrequestedDisconnectReason,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum UnrequestedDisconnectReason {
|
||||
#[error("client mixnet traffic timeout")]
|
||||
ClientMixnetTrafficTimeout,
|
||||
#[error("client tun traffic timeout")]
|
||||
ClientTunTrafficTimeout,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DataResponse {
|
||||
pub ip_packet: bytes::Bytes,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PongResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct HealthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: HealthResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct HealthResponseReply {
|
||||
// Return the binary build information of the IPR
|
||||
pub build_info: nym_bin_common::build_information::BinaryBuildInformationOwned,
|
||||
// Return if the IPR has performed a successful routing test.
|
||||
pub routable: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ErrorResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: ErrorResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum ErrorResponseReply {
|
||||
#[error("{msg}")]
|
||||
Generic { msg: String },
|
||||
#[error(
|
||||
"version mismatch: response is v{request_version} and response is v{response_version}"
|
||||
)]
|
||||
VersionMismatch {
|
||||
request_version: u8,
|
||||
response_version: u8,
|
||||
},
|
||||
#[error("destination failed exit policy filter check: {dst}")]
|
||||
ExitPolicyFilterCheckFailed { dst: String },
|
||||
}
|
||||
@@ -11,7 +11,6 @@ repository.workspace = true
|
||||
cfg-if = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
hex-literal = "0.3.3"
|
||||
log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"]}
|
||||
|
||||
@@ -400,25 +400,11 @@ fn fix_deprecated_environmental_variables() {
|
||||
}
|
||||
}
|
||||
|
||||
// Read the variables from the file and log what the corresponding values in the environment are.
|
||||
fn print_env_vars_with_keys_in_file<P: AsRef<Path> + Copy>(config_env_file: P) {
|
||||
let items = dotenvy::from_path_iter(config_env_file)
|
||||
.expect("Invalid path to environment configuration file");
|
||||
for item in items {
|
||||
let (key, val) = item.expect("Invalid item in environment configuration file");
|
||||
log::debug!("{}: {}", key, val);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_env<P: AsRef<Path>>(config_env_file: Option<P>) {
|
||||
match std::env::var(var_names::CONFIGURED) {
|
||||
// if the configuration is not already set in the env vars
|
||||
Err(std::env::VarError::NotPresent) => {
|
||||
if let Some(config_env_file) = &config_env_file {
|
||||
log::debug!(
|
||||
"Loading environment variables from {:?}",
|
||||
config_env_file.as_ref()
|
||||
);
|
||||
if let Some(config_env_file) = config_env_file {
|
||||
dotenvy::from_path(config_env_file)
|
||||
.expect("Invalid path to environment configuration file");
|
||||
fix_deprecated_environmental_variables();
|
||||
@@ -426,25 +412,17 @@ pub fn setup_env<P: AsRef<Path>>(config_env_file: Option<P>) {
|
||||
// if nothing is set, the use mainnet defaults
|
||||
// if the user has not set `CONFIGURED`, then even if they set any of the env variables,
|
||||
// overwrite them
|
||||
log::debug!("Loading mainnet defaults");
|
||||
crate::mainnet::export_to_env();
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
log::debug!("Environment variables already set. Using them");
|
||||
crate::mainnet::export_to_env()
|
||||
}
|
||||
Err(_) => crate::mainnet::export_to_env(),
|
||||
_ => {
|
||||
fix_deprecated_environmental_variables();
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't explicitly defined any of the constants, fallback to defaults
|
||||
crate::mainnet::export_to_env_if_not_set();
|
||||
|
||||
if let Some(config_env_file) = &config_env_file {
|
||||
print_env_vars_with_keys_in_file(config_env_file);
|
||||
}
|
||||
crate::mainnet::export_to_env_if_not_set()
|
||||
}
|
||||
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
|
||||
@@ -239,15 +239,8 @@ impl NymMessage {
|
||||
let (packets_used, space_left) =
|
||||
chunking::number_of_required_fragments(total_required_bytes, plaintext_per_packet);
|
||||
|
||||
let wasted_space_percentage =
|
||||
(space_left as f32 / (bytes.len() + 1 + space_left) as f32) * 100.0;
|
||||
log::trace!(
|
||||
"Padding {self_display}: {} of raw plaintext bytes are required. \
|
||||
They're going to be put into {packets_used} sphinx packets with {space_left} bytes \
|
||||
of leftover space. {wasted_space_percentage:.1}% of packet capacity is going to \
|
||||
be wasted.",
|
||||
bytes.len() + 1
|
||||
);
|
||||
let wasted_space = space_left as f32 / (bytes.len() + 1 + space_left) as f32;
|
||||
log::trace!("Padding {self_display}: {} of raw plaintext bytes are required. They're going to be put into {packets_used} sphinx packets with {space_left} bytes of leftover space. {wasted_space}% of packet capacity is going to be wasted.", bytes.len() + 1);
|
||||
|
||||
bytes
|
||||
.into_iter()
|
||||
|
||||
@@ -140,51 +140,17 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"commit_dealings_metadata"
|
||||
"commit_dealing"
|
||||
],
|
||||
"properties": {
|
||||
"commit_dealings_metadata": {
|
||||
"commit_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunks",
|
||||
"dealing_index",
|
||||
"dealing",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"chunks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DealingChunkInfo"
|
||||
}
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"commit_dealings_chunk"
|
||||
],
|
||||
"properties": {
|
||||
"commit_dealings_chunk": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"chunk": {
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
},
|
||||
"resharing": {
|
||||
@@ -235,7 +201,7 @@
|
||||
],
|
||||
"properties": {
|
||||
"owner": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
@@ -274,6 +240,10 @@
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -282,37 +252,17 @@
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"DealingChunkInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"size"
|
||||
],
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"data",
|
||||
"dealing_index"
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"dealing_index": {
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
@@ -457,66 +407,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealings_metadata"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealings_metadata": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealer_dealings_status"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealer_dealings_status": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -553,23 +443,17 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_chunk_status"
|
||||
"get_dealing"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_chunk_status": {
|
||||
"get_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -592,35 +476,39 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_chunk"
|
||||
"get_dealings"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_chunk": {
|
||||
"get_dealings": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"limit": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_after": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -1021,80 +909,6 @@
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"get_dealer_dealings_status": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealerDealingsStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"all_dealings_fully_submitted",
|
||||
"dealer",
|
||||
"dealing_submission_status",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"all_dealings_fully_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_submission_status": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DealingStatus"
|
||||
}
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DealingStatus": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_submission_status",
|
||||
"fully_submitted",
|
||||
"has_metadata"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_submission_status": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"fully_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"has_metadata": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealer_details": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealerDetailsResponse",
|
||||
@@ -1163,18 +977,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealing_chunk": {
|
||||
"get_dealing": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingChunkResponse",
|
||||
"title": "DealingResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"chunk": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
@@ -1184,14 +1000,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
@@ -1219,62 +1027,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealing_chunk_status": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingChunkStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealing_status": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingStatusResponse",
|
||||
@@ -1282,79 +1034,7 @@
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/DealingStatus"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DealingStatus": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_submission_status",
|
||||
"fully_submitted",
|
||||
"has_metadata"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_submission_status": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"fully_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"has_metadata": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealings_metadata": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingMetadataResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"dealing_submitted",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
@@ -1366,20 +1046,55 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealing_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealings": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealings",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
}
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DealingMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -1388,67 +1103,28 @@
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"DealingChunkInfo": {
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"size"
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DealingMetadata": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_index",
|
||||
"submitted_chunks"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_index": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"submitted_chunks": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/SubmittedChunk"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"SubmittedChunk": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"info",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/DealingChunkInfo"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -51,51 +51,17 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"commit_dealings_metadata"
|
||||
"commit_dealing"
|
||||
],
|
||||
"properties": {
|
||||
"commit_dealings_metadata": {
|
||||
"commit_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunks",
|
||||
"dealing_index",
|
||||
"dealing",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"chunks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DealingChunkInfo"
|
||||
}
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"commit_dealings_chunk"
|
||||
],
|
||||
"properties": {
|
||||
"commit_dealings_chunk": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"chunk": {
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
},
|
||||
"resharing": {
|
||||
@@ -146,7 +112,7 @@
|
||||
],
|
||||
"properties": {
|
||||
"owner": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
@@ -185,6 +151,10 @@
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -193,37 +163,17 @@
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"DealingChunkInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"size"
|
||||
],
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"data",
|
||||
"dealing_index"
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"dealing_index": {
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
|
||||
@@ -133,66 +133,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealings_metadata"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealings_metadata": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealer_dealings_status"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealer_dealings_status": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -229,23 +169,17 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_chunk_status"
|
||||
"get_dealing"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_chunk_status": {
|
||||
"get_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -268,35 +202,39 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_chunk"
|
||||
"get_dealings"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_chunk": {
|
||||
"get_dealings": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"limit": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_after": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealerDealingsStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"all_dealings_fully_submitted",
|
||||
"dealer",
|
||||
"dealing_submission_status",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"all_dealings_fully_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_submission_status": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/DealingStatus"
|
||||
}
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DealingStatus": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_submission_status",
|
||||
"fully_submitted",
|
||||
"has_metadata"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_submission_status": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"fully_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"has_metadata": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingChunkResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"chunk": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingChunkStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_index",
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_index": {
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id",
|
||||
"status"
|
||||
"dealing_submitted",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
@@ -17,13 +17,13 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealing_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/DealingStatus"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -31,43 +31,6 @@
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DealingStatus": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chunk_submission_status",
|
||||
"fully_submitted",
|
||||
"has_metadata"
|
||||
],
|
||||
"properties": {
|
||||
"chunk_submission_status": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"fully_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"has_metadata": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingMetadataResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DealingMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ChunkSubmissionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submission_height": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DealingChunkInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"size"
|
||||
],
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DealingMetadata": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_index",
|
||||
"submitted_chunks"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"submitted_chunks": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/SubmittedChunk"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"SubmittedChunk": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"info",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/DealingChunkInfo"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/ChunkSubmissionStatus"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,110 +121,233 @@ pub fn query_dealing_chunk(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::fixtures::{dealing_bytes_fixture, partial_dealing_fixture};
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::{Addr, DepsMut};
|
||||
use nym_coconut_dkg_common::dealing::{DealingChunkInfo, PartialContractDealing};
|
||||
|
||||
#[allow(unused)]
|
||||
fn fill_dealings(
|
||||
deps: DepsMut<'_>,
|
||||
epoch: EpochId,
|
||||
dealers: usize,
|
||||
key_size: u32,
|
||||
chunks: u16,
|
||||
) {
|
||||
for i in 0..dealers {
|
||||
let dealer = Addr::unchecked(format!("dealer{i}"));
|
||||
for dealing_index in 0..key_size {
|
||||
let data = dealing_bytes_fixture();
|
||||
let chunks = data.0.chunks(data.len() / chunks as usize);
|
||||
|
||||
let mut chunk_infos = Vec::new();
|
||||
for (chunk_index, chunk) in chunks.enumerate() {
|
||||
chunk_infos.push(DealingChunkInfo {
|
||||
size: chunk.len() as u64,
|
||||
});
|
||||
StoredDealing::save(
|
||||
deps.storage,
|
||||
epoch,
|
||||
&dealer,
|
||||
PartialContractDealing {
|
||||
dealing_index,
|
||||
chunk_index: chunk_index as ChunkIndex,
|
||||
data: chunk.into(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_dealing_chunk() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
let bad_address = "FOOMP".to_string();
|
||||
assert!(query_dealing_chunk(deps.as_ref(), 0, bad_address, 0, 0).is_err());
|
||||
|
||||
let empty = query_dealing_chunk(deps.as_ref(), 0, "foo".to_string(), 0, 0).unwrap();
|
||||
assert_eq!(empty.epoch_id, 0);
|
||||
assert_eq!(empty.dealing_index, 0);
|
||||
assert_eq!(empty.chunk_index, 0);
|
||||
assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
assert!(empty.chunk.is_none());
|
||||
|
||||
// insert the dealing chunk
|
||||
let dealing = partial_dealing_fixture();
|
||||
StoredDealing::save(
|
||||
deps.as_mut().storage,
|
||||
0,
|
||||
&Addr::unchecked("foo"),
|
||||
dealing.clone(),
|
||||
);
|
||||
|
||||
let retrieved = query_dealing_chunk(deps.as_ref(), 0, "foo".to_string(), 0, 0).unwrap();
|
||||
assert_eq!(retrieved.epoch_id, 0);
|
||||
assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
assert_eq!(retrieved.chunk_index, dealing.chunk_index);
|
||||
assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
assert_eq!(retrieved.chunk.unwrap(), dealing.data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_dealing_status() {
|
||||
let deps = init_contract();
|
||||
|
||||
let bad_address = "FOOMP".to_string();
|
||||
assert!(query_dealing_status(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
|
||||
let empty = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
assert_eq!(empty.epoch_id, 0);
|
||||
assert_eq!(empty.dealing_index, 0);
|
||||
assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
assert!(!empty.status.fully_submitted);
|
||||
assert!(!empty.status.has_metadata);
|
||||
assert!(empty.status.chunk_submission_status.is_empty());
|
||||
|
||||
// insert the metadata
|
||||
//
|
||||
|
||||
// // insert the dealing
|
||||
// let dealing = partial_dealing_fixture();
|
||||
// StoredDealing::save(
|
||||
// deps.as_mut().storage,
|
||||
// 0,
|
||||
// &Addr::unchecked("foo"),
|
||||
// dealing.clone(),
|
||||
// );
|
||||
//
|
||||
// let retrieved = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(retrieved.epoch_id, 0);
|
||||
// assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
// assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
// assert!(retrieved.dealing_submitted)
|
||||
}
|
||||
}
|
||||
// #[cfg(test)]
|
||||
// pub(crate) mod tests {
|
||||
// use super::*;
|
||||
// use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT};
|
||||
// use crate::support::tests::fixtures::{dealing_bytes_fixture, partial_dealing_fixture};
|
||||
// use crate::support::tests::helpers::init_contract;
|
||||
// use cosmwasm_std::{Addr, DepsMut};
|
||||
// use nym_coconut_dkg_common::types::PartialContractDealing;
|
||||
//
|
||||
// fn fill_dealings(deps: DepsMut<'_>, epoch: EpochId, dealers: usize, key_size: u32) {
|
||||
// for i in 0..dealers {
|
||||
// let dealer = Addr::unchecked(format!("dealer{i}"));
|
||||
// for dealing_index in 0..key_size {
|
||||
// StoredDealing::save(
|
||||
// deps.storage,
|
||||
// epoch,
|
||||
// &dealer,
|
||||
// PartialContractDealing {
|
||||
// dealing_index: dealing_index,
|
||||
// data: dealing_bytes_fixture(),
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_query_dealing() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// let bad_address = "FOOMP".to_string();
|
||||
// assert!(query_dealing(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
//
|
||||
// let empty = query_dealing(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(empty.epoch_id, 0);
|
||||
// assert_eq!(empty.dealing_index, 0);
|
||||
// assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
// assert!(empty.dealing.is_none());
|
||||
//
|
||||
// // insert the dealing
|
||||
// let dealing = partial_dealing_fixture();
|
||||
// StoredDealing::save(
|
||||
// deps.as_mut().storage,
|
||||
// 0,
|
||||
// &Addr::unchecked("foo"),
|
||||
// dealing.clone(),
|
||||
// );
|
||||
//
|
||||
// let retrieved = query_dealing(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(retrieved.epoch_id, 0);
|
||||
// assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
// assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
// assert_eq!(retrieved.dealing.unwrap(), dealing.data);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_query_dealing_status() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// let bad_address = "FOOMP".to_string();
|
||||
// assert!(query_dealing_status(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
//
|
||||
// let empty = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(empty.epoch_id, 0);
|
||||
// assert_eq!(empty.dealing_index, 0);
|
||||
// assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
// assert!(!empty.dealing_submitted);
|
||||
//
|
||||
// // insert the dealing
|
||||
// let dealing = partial_dealing_fixture();
|
||||
// StoredDealing::save(
|
||||
// deps.as_mut().storage,
|
||||
// 0,
|
||||
// &Addr::unchecked("foo"),
|
||||
// dealing.clone(),
|
||||
// );
|
||||
//
|
||||
// let retrieved = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(retrieved.epoch_id, 0);
|
||||
// assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
// assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
// assert!(retrieved.dealing_submitted)
|
||||
// }
|
||||
//
|
||||
// #[cfg(test)]
|
||||
// mod query_dealings {
|
||||
// use super::*;
|
||||
// use nym_coconut_dkg_common::types::DEFAULT_DEALINGS;
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_empty_on_init() {
|
||||
// let deps = init_contract();
|
||||
// let all_dealings = StoredDealing::unchecked_all_entries(&deps.storage);
|
||||
// assert!(all_dealings.is_empty())
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_obeys_limits() {
|
||||
// let mut deps = init_contract();
|
||||
// let limit = 2;
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(limit))
|
||||
// .unwrap();
|
||||
// assert_eq!(limit, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_has_default_limit() {
|
||||
// let mut deps = init_contract();
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // query without explicitly setting a limit
|
||||
// let page1 = query_dealings_paged(deps.as_ref(), 0, dealer, None, None).unwrap();
|
||||
//
|
||||
// assert_eq!(DEALINGS_PAGE_DEFAULT_LIMIT, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_has_max_limit() {
|
||||
// let mut deps = init_contract();
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// // query with a crazily high limit in an attempt to use too many resources
|
||||
// let crazy_limit = 1000 * DEALINGS_PAGE_MAX_LIMIT;
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(crazy_limit))
|
||||
// .unwrap();
|
||||
//
|
||||
// // we default to a decent sized upper bound instead
|
||||
// let expected_limit = DEALINGS_PAGE_MAX_LIMIT;
|
||||
// assert_eq!(expected_limit, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_pagination_works() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 0, 10, 1);
|
||||
// let per_page = 2;
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(per_page))
|
||||
// .unwrap();
|
||||
//
|
||||
// // page should have 1 result on it
|
||||
// assert_eq!(1, page1.dealings.len());
|
||||
// }
|
||||
//
|
||||
// // save another
|
||||
// fill_dealings(deps.as_mut(), 1, 10, 2);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // page1 should have 2 results on it
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 1, dealer, None, Option::from(per_page))
|
||||
// .unwrap();
|
||||
// assert_eq!(2, page1.dealings.len());
|
||||
// }
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 3, 10, 3);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // page1 still has 2 results
|
||||
// let page1 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 3,
|
||||
// dealer.clone(),
|
||||
// None,
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
// assert_eq!(2, page1.dealings.len());
|
||||
//
|
||||
// // retrieving the next page should start after the last key on this page
|
||||
// let start_after = page1.start_next_after.unwrap();
|
||||
// let page2 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 3,
|
||||
// dealer,
|
||||
// Option::from(start_after),
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// assert_eq!(1, page2.dealings.len());
|
||||
// }
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 4, 10, 4);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 4,
|
||||
// dealer.clone(),
|
||||
// None,
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
// let start_after = page1.start_next_after.unwrap();
|
||||
// let page2 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 4,
|
||||
// dealer,
|
||||
// Option::from(start_after),
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// // now we have 2 pages, with 2 results on the second page
|
||||
// assert_eq!(2, page2.dealings.len());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::ContractError;
|
||||
use cosmwasm_std::{Addr, Storage};
|
||||
use cw_storage_plus::{Key, Map, Path, PrimaryKey};
|
||||
use cosmwasm_std::{Addr, Order, Record, StdResult, Storage};
|
||||
use cw_storage_plus::{Bound, Key, KeyDeserialize, Map, Path, Prefix, Prefixer, PrimaryKey};
|
||||
use nym_coconut_dkg_common::dealing::{DealingMetadata, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ChunkIndex, ContractSafeBytes, DealingIndex, EpochId, PartialContractDealingData,
|
||||
@@ -24,6 +24,15 @@ pub(crate) fn metadata_exists(
|
||||
DEALINGS_METADATA.has(storage, (epoch_id, dealer, dealing_index))
|
||||
}
|
||||
|
||||
pub(crate) fn may_read_metadata(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<Option<DealingMetadata>, ContractError> {
|
||||
Ok(DEALINGS_METADATA.may_load(storage, (epoch_id, dealer, dealing_index))?)
|
||||
}
|
||||
|
||||
pub(crate) fn must_read_metadata(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
@@ -62,54 +71,16 @@ pub(crate) struct StoredDealing;
|
||||
impl StoredDealing {
|
||||
const NAMESPACE: &'static [u8] = b"dealing";
|
||||
|
||||
// prefix-range related should we need it
|
||||
#[cfg(test)]
|
||||
fn deserialize_dealing_record(
|
||||
kv: cosmwasm_std::Record,
|
||||
) -> cosmwasm_std::StdResult<(ChunkIndex, PartialContractDealingData)> {
|
||||
kv: Record,
|
||||
) -> StdResult<(ChunkIndex, PartialContractDealingData)> {
|
||||
let (k, v) = kv;
|
||||
let index = <ChunkIndex as cw_storage_plus::KeyDeserialize>::from_vec(k)?;
|
||||
let index = <ChunkIndex as KeyDeserialize>::from_vec(k)?;
|
||||
let data = ContractSafeBytes(v);
|
||||
|
||||
Ok((index, data))
|
||||
}
|
||||
|
||||
// prefix-range related should we need it
|
||||
#[cfg(test)]
|
||||
fn prefix(
|
||||
prefix: (EpochId, Dealer, DealingIndex),
|
||||
) -> cw_storage_plus::Prefix<ChunkIndex, PartialContractDealingData, ChunkIndex> {
|
||||
use cw_storage_plus::Prefixer;
|
||||
|
||||
cw_storage_plus::Prefix::with_deserialization_functions(
|
||||
Self::NAMESPACE,
|
||||
&prefix.prefix(),
|
||||
&[],
|
||||
// explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data
|
||||
|_, _, kv| Self::deserialize_dealing_record(kv),
|
||||
|_, _, _| panic!("attempted to call custom de_fn_v"),
|
||||
)
|
||||
}
|
||||
|
||||
// prefix-range related should we need it
|
||||
#[cfg(test)]
|
||||
pub(crate) fn prefix_range<'a>(
|
||||
storage: &'a dyn Storage,
|
||||
prefix: (EpochId, Dealer, DealingIndex),
|
||||
start: Option<cw_storage_plus::Bound<ChunkIndex>>,
|
||||
) -> impl Iterator<Item = cosmwasm_std::StdResult<PartialContractDealing>> + 'a {
|
||||
let dealing_index = prefix.2;
|
||||
Self::prefix(prefix)
|
||||
.range(storage, start, None, cosmwasm_std::Order::Ascending)
|
||||
.map(move |maybe_record| {
|
||||
maybe_record.map(|(chunk_index, data)| PartialContractDealing {
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
data,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn storage_key(
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
@@ -126,26 +97,39 @@ impl StoredDealing {
|
||||
)
|
||||
}
|
||||
|
||||
// pub(crate) fn exists(
|
||||
// storage: &dyn Storage,
|
||||
// epoch_id: EpochId,
|
||||
// dealer: &Addr,
|
||||
// dealing_index: DealingIndex,
|
||||
// chunk_index: ChunkIndex,
|
||||
// ) -> StdResult<bool> {
|
||||
// // whenever the dealing is saved, the metadata is appropriately updated
|
||||
// // reading metadata is way cheaper than the dealing chunk itself
|
||||
// let Some(metadata) =
|
||||
// DEALINGS_METADATA.may_load(storage, (epoch_id, dealer, dealing_index))?
|
||||
// else {
|
||||
// return Ok(false);
|
||||
// };
|
||||
// let Some(chunk_info) = metadata.submitted_chunks.get(&chunk_index) else {
|
||||
// return Ok(false);
|
||||
// };
|
||||
// Ok(chunk_info.status.submitted())
|
||||
// // StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
|
||||
// }
|
||||
fn prefix(
|
||||
prefix: (EpochId, Dealer, DealingIndex),
|
||||
) -> Prefix<ChunkIndex, PartialContractDealingData, ChunkIndex> {
|
||||
Prefix::with_deserialization_functions(
|
||||
Self::NAMESPACE,
|
||||
&prefix.prefix(),
|
||||
&[],
|
||||
// explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data
|
||||
|_, _, kv| Self::deserialize_dealing_record(kv),
|
||||
|_, _, _| panic!("attempted to call custom de_fn_v"),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn exists(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: &Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> StdResult<bool> {
|
||||
// whenever the dealing is saved, the metadata is appropriately updated
|
||||
// reading metadata is way cheaper than the dealing chunk itself
|
||||
let Some(metadata) =
|
||||
DEALINGS_METADATA.may_load(storage, (epoch_id, dealer, dealing_index))?
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
let Some(chunk_info) = metadata.submitted_chunks.get(&chunk_index) else {
|
||||
return Ok(false);
|
||||
};
|
||||
Ok(chunk_info.status.submitted())
|
||||
// StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
|
||||
}
|
||||
|
||||
pub(crate) fn save(
|
||||
storage: &mut dyn Storage,
|
||||
@@ -174,6 +158,23 @@ impl StoredDealing {
|
||||
storage.get(&storage_key).map(ContractSafeBytes)
|
||||
}
|
||||
|
||||
pub(crate) fn prefix_range<'a>(
|
||||
storage: &'a dyn Storage,
|
||||
prefix: (EpochId, Dealer, DealingIndex),
|
||||
start: Option<Bound<ChunkIndex>>,
|
||||
) -> impl Iterator<Item = StdResult<PartialContractDealing>> + 'a {
|
||||
let dealing_index = prefix.2;
|
||||
Self::prefix(prefix)
|
||||
.range(storage, start, None, Order::Ascending)
|
||||
.map(move |maybe_record| {
|
||||
maybe_record.map(|(chunk_index, data)| PartialContractDealing {
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
data,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// iterate over all values, only to be used in tests due to the amount of data being returned
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
@@ -183,25 +184,20 @@ impl StoredDealing {
|
||||
(EpochId, Addr, (DealingIndex, ChunkIndex)),
|
||||
PartialContractDealingData,
|
||||
)> {
|
||||
use cw_storage_plus::KeyDeserialize;
|
||||
|
||||
type StorageKey<'a> = (EpochId, Dealer<'a>, (DealingIndex, ChunkIndex));
|
||||
|
||||
let empty_prefix: cw_storage_plus::Prefix<
|
||||
StorageKey,
|
||||
PartialContractDealingData,
|
||||
StorageKey,
|
||||
> = cw_storage_plus::Prefix::with_deserialization_functions(
|
||||
Self::NAMESPACE,
|
||||
&[],
|
||||
&[],
|
||||
|_, _, kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))),
|
||||
|_, _, _| unimplemented!(),
|
||||
);
|
||||
let empty_prefix: Prefix<StorageKey, PartialContractDealingData, StorageKey> =
|
||||
Prefix::with_deserialization_functions(
|
||||
Self::NAMESPACE,
|
||||
&[],
|
||||
&[],
|
||||
|_, _, kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))),
|
||||
|_, _, _| unimplemented!(),
|
||||
);
|
||||
|
||||
empty_prefix
|
||||
.range(storage, None, None, cosmwasm_std::Order::Ascending)
|
||||
.collect::<cosmwasm_std::StdResult<_>>()
|
||||
.range(storage, None, None, Order::Ascending)
|
||||
.collect::<StdResult<_>>()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
@@ -210,7 +206,6 @@ impl StoredDealing {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cw_storage_plus::Bound;
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn dealing_data(
|
||||
|
||||
@@ -175,14 +175,14 @@ pub fn try_commit_dealings_chunk(
|
||||
}
|
||||
|
||||
// check if the received chunk has the specified size
|
||||
if submission_status.info.size != chunk.data.len() as u64 {
|
||||
if submission_status.info.size != chunk.data.len() {
|
||||
return Err(ContractError::InconsistentChunkLength {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index: chunk.dealing_index,
|
||||
chunk_index: chunk.chunk_index,
|
||||
metadata_length: submission_status.info.size,
|
||||
received: chunk.data.len() as u64,
|
||||
received: chunk.data.len(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -207,9 +207,7 @@ pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::fixtures::{
|
||||
dealer_details_fixture, dealing_metadata_fixture, partial_dealing_fixture,
|
||||
};
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, partial_dealing_fixture};
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
@@ -220,185 +218,129 @@ pub(crate) mod tests {
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn invalid_commit_dealing_chunk() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let mut env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let owner = Addr::unchecked("owner1");
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealing = partial_dealing_fixture();
|
||||
|
||||
let ret = try_commit_dealings_chunk(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
dealing.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
add_fixture_dealer(deps.as_mut());
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let ret = try_commit_dealings_chunk(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
dealing.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotADealer);
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: owner.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
dealers_storage::current_dealers()
|
||||
.save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
.unwrap();
|
||||
|
||||
// assume we're in resharing mode
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.state = EpochState::DealingExchange { resharing: true };
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&InitialReplacementData {
|
||||
initial_dealers: vec![],
|
||||
initial_height: 1,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let ret = try_commit_dealings_chunk(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
dealing.clone(),
|
||||
true,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
data.initial_dealers = vec![dealer_details_fixture(1).address];
|
||||
Ok(data)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// back to 'normal' mode
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.state = EpochState::DealingExchange { resharing: false };
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// TODO: test case: no metadata
|
||||
fn invalid_commit_dealing() {
|
||||
todo!()
|
||||
// let mut deps = helpers::init_contract();
|
||||
// let mut env = mock_env();
|
||||
// try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
//
|
||||
// let owner = Addr::unchecked("owner1");
|
||||
// let info = mock_info(owner.as_str(), &[]);
|
||||
// let dealing = partial_dealing_fixture();
|
||||
//
|
||||
|
||||
// add dealing metadata
|
||||
try_submit_dealings_metadata(
|
||||
deps.as_mut(),
|
||||
info.clone(),
|
||||
0,
|
||||
dealing_metadata_fixture(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// dealing chunk out of range
|
||||
let ret = try_commit_dealings_chunk(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
PartialContractDealing {
|
||||
dealing_index: 0,
|
||||
chunk_index: 42,
|
||||
data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
},
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::DealingChunkNotInMetadata {
|
||||
epoch_id: 0,
|
||||
dealer: info.sender.clone(),
|
||||
dealing_index: 0,
|
||||
chunk_index: 42,
|
||||
}
|
||||
);
|
||||
|
||||
// 'good' dealing
|
||||
let ret = try_commit_dealings_chunk(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
dealing.clone(),
|
||||
false,
|
||||
);
|
||||
assert!(ret.is_ok());
|
||||
|
||||
// duplicate dealing
|
||||
let ret = try_commit_dealings_chunk(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
dealing.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::DealingChunkAlreadyCommitted {
|
||||
epoch_id: 0,
|
||||
dealer: info.sender.clone(),
|
||||
dealing_index: 0,
|
||||
chunk_index: 0,
|
||||
block_height: env.block.height,
|
||||
}
|
||||
);
|
||||
|
||||
// same index, but next epoch
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.epoch_id += 1;
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
try_submit_dealings_metadata(
|
||||
deps.as_mut(),
|
||||
info.clone(),
|
||||
0,
|
||||
dealing_metadata_fixture(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let ret = try_commit_dealings_chunk(deps.as_mut(), env, info, dealing.clone(), false);
|
||||
assert!(ret.is_ok());
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::IncorrectEpochState {
|
||||
// current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
// expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// env.block.time = env
|
||||
// .block
|
||||
// .time
|
||||
// .plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
// add_fixture_dealer(deps.as_mut());
|
||||
// advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
//
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(ret, ContractError::NotADealer);
|
||||
//
|
||||
// let dealer_details = DealerDetails {
|
||||
// address: owner.clone(),
|
||||
// bte_public_key_with_proof: String::new(),
|
||||
// ed25519_identity: String::new(),
|
||||
// announce_address: String::new(),
|
||||
// assigned_index: 1,
|
||||
// };
|
||||
// dealers_storage::current_dealers()
|
||||
// .save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
// .unwrap();
|
||||
//
|
||||
// // assume we're in resharing mode
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.state = EpochState::DealingExchange { resharing: true };
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
// INITIAL_REPLACEMENT_DATA
|
||||
// .save(
|
||||
// deps.as_mut().storage,
|
||||
// &InitialReplacementData {
|
||||
// initial_dealers: vec![],
|
||||
// initial_height: 1,
|
||||
// },
|
||||
// )
|
||||
// .unwrap();
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), true).unwrap_err();
|
||||
// assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
//
|
||||
// INITIAL_REPLACEMENT_DATA
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
// data.initial_dealers = vec![dealer_details_fixture(1).address];
|
||||
// Ok(data)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// // back to 'normal' mode
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.state = EpochState::DealingExchange { resharing: false };
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// // dealing out of range
|
||||
// let ret = try_commit_dealings(
|
||||
// deps.as_mut(),
|
||||
// info.clone(),
|
||||
// PartialContractDealing {
|
||||
// dealing_index: 42,
|
||||
// data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
// },
|
||||
// false,
|
||||
// )
|
||||
// .unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::DealingOutOfRange {
|
||||
// epoch_id: 0,
|
||||
// dealer: info.sender.clone(),
|
||||
// index: 42,
|
||||
// key_size: DEFAULT_DEALINGS as u32,
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// // 'good' dealing
|
||||
// let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
// assert!(ret.is_ok());
|
||||
//
|
||||
// // duplicate dealing
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::DealingAlreadyCommitted {
|
||||
// epoch_id: 0,
|
||||
// dealer: info.sender.clone(),
|
||||
// index: 0,
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// // same index, but next epoch
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.epoch_id += 1;
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
// assert!(ret.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ pub enum ContractError {
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
first_chunk_size: u64,
|
||||
size: u64,
|
||||
first_chunk_size: usize,
|
||||
size: usize,
|
||||
},
|
||||
|
||||
#[error("the received chunk for epoch {epoch_id} from dealer {dealer} at dealing index {dealing_index} at chunk index {chunk_index} has inconsistent length. the metadata contains length of {metadata_length} while the received data is {received} bytes long")]
|
||||
@@ -100,8 +100,8 @@ pub enum ContractError {
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
metadata_length: u64,
|
||||
received: u64,
|
||||
metadata_length: usize,
|
||||
received: usize,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} has attempted to commit dealing metadata for epoch {epoch_id} for dealing index {dealing_index} zero chunks")]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::dealing::{DealingChunkInfo, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::dealing::PartialContractDealing;
|
||||
use nym_coconut_dkg_common::types::ContractSafeBytes;
|
||||
use nym_coconut_dkg_common::verification_key::ContractVKShare;
|
||||
|
||||
@@ -20,26 +20,18 @@ pub fn vk_share_fixture(owner: &str, index: u64) -> ContractVKShare {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn dealing_bytes_fixture() -> ContractSafeBytes {
|
||||
ContractSafeBytes(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
ContractSafeBytes(vec![1, 2, 3])
|
||||
}
|
||||
|
||||
pub fn partial_dealing_fixture() -> PartialContractDealing {
|
||||
PartialContractDealing {
|
||||
chunk_index: 0,
|
||||
dealing_index: 0,
|
||||
data: ContractSafeBytes(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
|
||||
data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dealing_metadata_fixture() -> Vec<DealingChunkInfo> {
|
||||
let chunk_fixture = partial_dealing_fixture();
|
||||
vec![DealingChunkInfo {
|
||||
size: chunk_fixture.data.len() as u64,
|
||||
}]
|
||||
}
|
||||
|
||||
pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails {
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(format!("owner{}", assigned_index)),
|
||||
|
||||
@@ -4,10 +4,9 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
links = "cpucycles"
|
||||
license = "LicenseRef-PD-hp OR CC0-1.0 OR 0BSD OR MIT-0 OR MIT"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.140"
|
||||
|
||||
[build-dependencies]
|
||||
cfg-if = "1"
|
||||
cfg-if = "1"
|
||||
@@ -114,7 +114,6 @@ allow = [
|
||||
"CC0-1.0",
|
||||
"Unicode-DFS-2016",
|
||||
"OpenSSL",
|
||||
"Zlib",
|
||||
]
|
||||
# List of explicitly disallowed licenses
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
|
||||
@@ -1,48 +1,64 @@
|
||||
[book]
|
||||
title = "Nym Docs"
|
||||
title = "Nym Developer Portal"
|
||||
authors = ["Max Hampshire, Serinko, Alexia Lorenza Martinel"]
|
||||
description = "Nym technical documentation"
|
||||
language = "en"
|
||||
multilingual = false # for the moment - ideally work on chinese, brazillian ,portugese spanish next
|
||||
multilingual = false
|
||||
src = "src"
|
||||
|
||||
[rust]
|
||||
edition = "2018"
|
||||
|
||||
#################
|
||||
# PREPROCESSORS #
|
||||
#################
|
||||
|
||||
# Note if changing these fields:
|
||||
# - make sure you aren't running `mdbook serve`
|
||||
# - change value of `turn-off = true` to `false`. This is set to `true` usually to avoid a known constant-reload bug with `mdbook serve` and this preprocessor
|
||||
# - delete `./theme/` dir
|
||||
# - change the preprocessor values that you want to edit
|
||||
# - run `mdbook build`: this will rebuild the `./theme` directory
|
||||
# - change value of `turn-off` back to `true`
|
||||
[preprocessor.theme]
|
||||
sidebar-width = "280px"
|
||||
content-max-width = "70%"
|
||||
content-main-margin-left = "5%"
|
||||
content-main-margin-right = "5%"
|
||||
root-font-size = "70%"
|
||||
# DO NOT CHANGE or you might overwrite the custom hbs file
|
||||
pagetoc = true
|
||||
# some variables related (defined in theme/css/variables.css)
|
||||
# `content-max-width` + `pagetoc-width` = 95% seems the best
|
||||
pagetoc-width = "13%"
|
||||
content-max-width = "70%" # 82
|
||||
pagetoc-fontsize = "14.5px"
|
||||
sidebar-width = "300px"
|
||||
menu-bar-height = "40px" # memu-bar = the bar on the top
|
||||
page-padding = "20px"
|
||||
mobile-content-max-width = "98%"
|
||||
|
||||
# layout
|
||||
content-padding = "0 10px"
|
||||
content-main-margin-left = "10%"
|
||||
content-main-margin-right = "10%"
|
||||
nav-chapters-max-width = "auto"
|
||||
nav-chapters-min-width = "auto"
|
||||
chapter-line-height = "2em"
|
||||
section-line-height = "1.5em"
|
||||
|
||||
# if true, never read and touch the files in theme dir
|
||||
turn-off = true
|
||||
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
|
||||
# variables preprocessor: import variables into files
|
||||
# https://gitlab.com/tglman/mdbook-variables/
|
||||
[preprocessor.variables.variables]
|
||||
minimum_rust_version = "1.66"
|
||||
wallet_release_version = "1.2.8"
|
||||
# nym-vpn related variables
|
||||
nym_vpn_latest_binary_url = "https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.3"
|
||||
nym_vpn_form_url = "https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2"
|
||||
|
||||
[preprocessor.last-changed]
|
||||
command = "mdbook-last-changed"
|
||||
renderer = ["html"]
|
||||
|
||||
# used for grabbing output of binary commands for automation https://github.com/FauconFan/mdbook-cmdrun
|
||||
# used for grabbing output of binary commands for automation
|
||||
# https://github.com/FauconFan/mdbook-cmdrun
|
||||
[preprocessor.cmdrun]
|
||||
|
||||
# more pre-processor plugins to look into from https://github.com/rust-lang/mdBook/wiki/Third-party-plugins & https://lib.rs/keywords/mdbook-preprocessor
|
||||
# mdbook-i18n
|
||||
|
||||
#########
|
||||
# BUILD #
|
||||
@@ -59,16 +75,19 @@ extra-watch-dirs = [] # directories to watch for triggering builds
|
||||
##########
|
||||
|
||||
[output.html]
|
||||
theme = "nym_themes"
|
||||
default-theme = "coal"
|
||||
preferred-dark-theme = "coal"
|
||||
curly-quotes = true
|
||||
# mathjax-support = false # useful if we want to pull equations in ?
|
||||
copy-fonts = true
|
||||
no-section-label = false
|
||||
additional-css = ["./nym_themes/custom.css", "./nym_themes/mdbook-admonish.css", "./nym_themes/pagetoc.css"]
|
||||
additional-js = ["./nym_themes/pagetoc.js"]
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "custom.css", "./mdbook-admonish.css"]
|
||||
additional-js = ["theme/pagetoc.js"]
|
||||
git-repository-url = "https://github.com/nymtech/nym"
|
||||
git-repository-icon = "fa-github"
|
||||
# edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
|
||||
site-url = "/developers/"
|
||||
# cname = "nymtech.net"
|
||||
input-404 = "not-found.md"
|
||||
|
||||
[output.html.fold]
|
||||
@@ -80,7 +99,7 @@ level = 0 # the depth to start folding
|
||||
editable = false # allows editing the source code
|
||||
copyable = true # include the copy button for copying code snippets
|
||||
copy-js = true # includes the JavaScript for the code editor
|
||||
line-numbers = true # displays line numbers for editable code
|
||||
line-numbers = false # displays line numbers for editable code
|
||||
runnable = true # displays a run button for rust code
|
||||
|
||||
# options for the built in text search
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
:root {
|
||||
--mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace;
|
||||
}
|
||||
.coal {
|
||||
--bg: #121726;
|
||||
--fg: #f2f2f2;
|
||||
|
||||
--sidebar-bg: #121726;
|
||||
--sidebar-fg: #f2f2f2;
|
||||
--sidebar-active: #fb6e4e;
|
||||
|
||||
--icons: #f2f2f2;
|
||||
--icons-hover: #fb6e4e;
|
||||
|
||||
--links: #fb6e4e;
|
||||
}
|
||||
|
||||
.light {
|
||||
--bg: #f4f6f8;
|
||||
--fg: #121726;
|
||||
|
||||
--sidebar-bg: #f4f6f8;
|
||||
--sidebar-fg: #121726;
|
||||
--sidebar-active: #fb6e4e;
|
||||
|
||||
--icons: #121726;
|
||||
--icons-hover: #fb6e4e;
|
||||
|
||||
--links: #fb6e4e;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
border-top: 1px solid black;
|
||||
padding: 5px 0;
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
|
||||
/* Globals */
|
||||
|
||||
:root {
|
||||
--sidebar-width: 280px;
|
||||
--sidebar-resize-indicator-width: 8px;
|
||||
--sidebar-resize-indicator-space: 2px;
|
||||
--page-padding: 15px;
|
||||
--content-max-width: 80%;
|
||||
--menu-bar-height: 40px;
|
||||
--mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
|
||||
--code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */
|
||||
--pagetoc-width: 13%;
|
||||
--pagetoc-fontsize: 14.5px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width:1439px) {
|
||||
:root{
|
||||
--content-max-width: 98%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Themes */
|
||||
|
||||
.ayu {
|
||||
--bg: hsl(210, 25%, 8%);
|
||||
--fg: #c5c5c5;
|
||||
|
||||
--sidebar-bg: #14191f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #5c6773;
|
||||
--sidebar-active: #ffb454;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #0096cf;
|
||||
|
||||
--inline-code-color: #ffb454;
|
||||
|
||||
--theme-popup-bg: #14191f;
|
||||
--theme-popup-border: #5c6773;
|
||||
--theme-hover: #191f26;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(210, 25%, 13%);
|
||||
--table-header-bg: hsl(210, 25%, 28%);
|
||||
--table-alternate-bg: hsl(210, 25%, 11%);
|
||||
|
||||
--searchbar-border-color: #848484;
|
||||
--searchbar-bg: #424242;
|
||||
--searchbar-fg: #fff;
|
||||
--searchbar-shadow-color: #d4c89f;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #252932;
|
||||
--search-mark-bg: #e3b171;
|
||||
|
||||
--color-scheme: dark;
|
||||
}
|
||||
|
||||
.coal {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
|
||||
--color-scheme: dark;
|
||||
}
|
||||
|
||||
.light {
|
||||
--bg: hsl(0, 0%, 100%);
|
||||
--fg: hsl(0, 0%, 0%);
|
||||
|
||||
--sidebar-bg: #fafafa;
|
||||
--sidebar-fg: hsl(0, 0%, 0%);
|
||||
--sidebar-non-existant: #aaaaaa;
|
||||
--sidebar-active: #1f1fff;
|
||||
--sidebar-spacer: #f4f4f4;
|
||||
|
||||
--scrollbar: #8F8F8F;
|
||||
|
||||
--icons: #747474;
|
||||
--icons-hover: #000000;
|
||||
|
||||
--links: #1f1fff;
|
||||
|
||||
--inline-code-color: #F42C4C;
|
||||
|
||||
--theme-popup-bg: #fafafa;
|
||||
--theme-popup-border: #cccccc;
|
||||
--theme-hover: #e6e6e6;
|
||||
|
||||
--quote-bg: hsl(197, 37%, 96%);
|
||||
--quote-border: hsl(197, 37%, 91%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(0, 0%, 95%);
|
||||
--table-header-bg: hsl(0, 0%, 80%);
|
||||
--table-alternate-bg: hsl(0, 0%, 97%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #e4f2fe;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--color-scheme: light;
|
||||
}
|
||||
|
||||
.navy {
|
||||
--bg: hsl(226, 23%, 11%);
|
||||
--fg: #bcbdd0;
|
||||
|
||||
--sidebar-bg: #282d3f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505274;
|
||||
--sidebar-active: #2b79a2;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: #282e40;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(226, 23%, 16%);
|
||||
--table-header-bg: hsl(226, 23%, 31%);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--color-scheme: dark;
|
||||
}
|
||||
|
||||
.rust {
|
||||
--bg: hsl(60, 9%, 87%);
|
||||
--fg: #262625;
|
||||
|
||||
--sidebar-bg: #3b2e2a;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #e69f67;
|
||||
--sidebar-spacer: #45373a;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #262625;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #6e6b5e;
|
||||
|
||||
--theme-popup-bg: #e1e1db;
|
||||
--theme-popup-border: #b38f6b;
|
||||
--theme-hover: #99908a;
|
||||
|
||||
--quote-bg: hsl(60, 5%, 75%);
|
||||
--quote-border: hsl(60, 5%, 70%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(60, 9%, 82%);
|
||||
--table-header-bg: #b3a497;
|
||||
--table-alternate-bg: hsl(60, 9%, 84%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #dec2a2;
|
||||
--search-mark-bg: #e69f67;
|
||||
|
||||
--color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.light.no-js {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
:root {
|
||||
--mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace;
|
||||
}
|
||||
|
||||
.coal {
|
||||
--fg: #f2f2f2;
|
||||
--sidebar-fg: #f2f2f2;
|
||||
--sidebar-active: #fb6e4e;
|
||||
--icons-hover: #fb6e4e;
|
||||
--links: #fb6e4e;
|
||||
::selection {
|
||||
color: #121726;
|
||||
background-color: #c5573d;
|
||||
}
|
||||
select option {
|
||||
background-color: #121726;
|
||||
}
|
||||
}
|
||||
|
||||
.light {
|
||||
--sidebar-active: #fb6e4e;
|
||||
--icons-hover: #fb6e4e;
|
||||
--links: #fb6e4e;
|
||||
::selection {
|
||||
color:#f2f2f2;
|
||||
background-color: #c5573d;
|
||||
}
|
||||
}
|
||||
|
||||
/*properly centering the title given the additional header items*/
|
||||
.menu-title {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.menu-bar .a:hover,
|
||||
|
||||
/*necessary because of ^*/
|
||||
.right-buttons {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
select {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
border-top: 1px solid black;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="{{ language }}" class="{{ default_theme }}" dir="{{ text_direction }}">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ title }}</title>
|
||||
{{#if is_print }}
|
||||
<meta name="robots" content="noindex">
|
||||
{{/if}}
|
||||
{{#if base_url}}
|
||||
<base href="{{ base_url }}">
|
||||
{{/if}}
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
{{> head}}
|
||||
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
{{#if favicon_svg}}
|
||||
<link rel="icon" href="{{ path_to_root }}favicon.svg">
|
||||
{{/if}}
|
||||
{{#if favicon_png}}
|
||||
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
|
||||
{{/if}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
|
||||
{{#if print_enable}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
|
||||
{{/if}}
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
|
||||
{{#if copy_fonts}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
|
||||
{{/if}}
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
{{#each additional_css}}
|
||||
<link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
|
||||
{{/each}}
|
||||
|
||||
<!-- {{#if mathjax_support}}-->
|
||||
<!-- <!– MathJax –>-->
|
||||
<!-- <script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>-->
|
||||
<!-- {{/if}}-->
|
||||
</head>
|
||||
<body class="sidebar-visible no-js">
|
||||
<div id="body-container">
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "{{ path_to_root }}";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('{{ default_theme }}')
|
||||
html.classList.add(theme);
|
||||
var body = document.querySelector('body');
|
||||
body.classList.remove('no-js')
|
||||
body.classList.add('js');
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var body = document.querySelector('body');
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
body.classList.remove('sidebar-visible');
|
||||
body.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
{{#toc}}{{/toc}}
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Track and set sidebar scroll position -->
|
||||
<script>
|
||||
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
||||
sidebarScrollbox.addEventListener('click', function(e) {
|
||||
if (e.target.tagName === 'A') {
|
||||
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
||||
}
|
||||
}, { passive: true });
|
||||
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
||||
sessionStorage.removeItem('sidebar-scroll');
|
||||
if (sidebarScrollTop) {
|
||||
// preserve sidebar scroll position when navigating via links within sidebar
|
||||
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
||||
} else {
|
||||
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
||||
var activeSection = document.querySelector('#sidebar .active');
|
||||
if (activeSection) {
|
||||
activeSection.scrollIntoView({ block: 'center' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
{{> header}}
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Dark</button></li>
|
||||
</ul>
|
||||
|
||||
<!-- CUSTOM -->
|
||||
<select id="dropdown-menu3" class="icon-button">
|
||||
<option value="">Network Protocol</option>
|
||||
<option value="https://nymtech.net/docs">Network Docs</option>
|
||||
<option value="https://nymtech.net/learn/papers">Academic Papers</option>
|
||||
</select>
|
||||
|
||||
<select id="dropdown-menu" class="icon-button">
|
||||
<option value="">Developer Docs</option>
|
||||
<option value="https://nymtech.net/developers">Dev Portal</option>
|
||||
<option value="https://nymtech.net/docs/sdk/rust/rust.html">Rust SDK</option>
|
||||
<option value="https://sdk.nymtech.net">Typescript SDK</option>
|
||||
</select>
|
||||
|
||||
<select id="dropdown-menu2" class="icon-button">
|
||||
<option value="">Setup Guides</option>
|
||||
<option value="https://nymtech.net/operators">Operators</option>
|
||||
<option value="https://nymtech.net/developers/nymvpn/intro.html">NymVPN Testing</option>
|
||||
</select>
|
||||
|
||||
<script>
|
||||
document.getElementById('dropdown-menu').addEventListener('change', function() {
|
||||
const selected = this.options[this.selectedIndex];
|
||||
if (selected.value !== '') {
|
||||
window.location.href = selected.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.getElementById('dropdown-menu2').addEventListener('change', function() {
|
||||
const selected = this.options[this.selectedIndex];
|
||||
if (selected.value !== '') {
|
||||
window.location.href = selected.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.getElementById('dropdown-menu3').addEventListener('change', function() {
|
||||
const selected = this.options[this.selectedIndex];
|
||||
if (selected.value !== '') {
|
||||
window.location.href = selected.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- END CUSTOM -->
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"> {{ book_title }}</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
{{#if search_enabled}}
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if print_enable}}
|
||||
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if git_repository_url}}
|
||||
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if git_repository_edit_url}}
|
||||
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if search_enabled}}
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<main>
|
||||
<div class="content-wrap">
|
||||
{{{ content }}}
|
||||
</div>
|
||||
<div class="sidetoc">
|
||||
<nav class="pagetoc"></nav>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
{{#if live_reload_endpoint}}
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_line_numbers}}
|
||||
<script>
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_copyable}}
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_js}}
|
||||
<script src="{{ path_to_root }}ace.js"></script>
|
||||
<script src="{{ path_to_root }}editor.js"></script>
|
||||
<script src="{{ path_to_root }}mode-rust.js"></script>
|
||||
<script src="{{ path_to_root }}theme-dawn.js"></script>
|
||||
<script src="{{ path_to_root }}theme-tomorrow_night.js"></script>
|
||||
{{/if}}
|
||||
|
||||
{{#if search_js}}
|
||||
<script src="{{ path_to_root }}elasticlunr.min.js"></script>
|
||||
<script src="{{ path_to_root }}mark.min.js"></script>
|
||||
<script src="{{ path_to_root }}searcher.js"></script>
|
||||
{{/if}}
|
||||
|
||||
<script src="{{ path_to_root }}clipboard.min.js"></script>
|
||||
<script src="{{ path_to_root }}highlight.js"></script>
|
||||
<script src="{{ path_to_root }}book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
{{#each additional_js}}
|
||||
<script src="{{ ../path_to_root }}{{this}}"></script>
|
||||
{{/each}}
|
||||
|
||||
{{#if is_print}}
|
||||
{{#if mathjax_support}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
MathJax.Hub.Register.StartupHook('End', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{else}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
</script>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,370 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="{{ language }}" class="{{ default_theme }}" dir="{{ text_direction }}">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ title }}</title>
|
||||
{{#if is_print }}
|
||||
<meta name="robots" content="noindex">
|
||||
{{/if}}
|
||||
{{#if base_url}}
|
||||
<base href="{{ base_url }}">
|
||||
{{/if}}
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
{{> head}}
|
||||
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
{{#if favicon_svg}}
|
||||
<link rel="icon" href="{{ path_to_root }}favicon.svg">
|
||||
{{/if}}
|
||||
{{#if favicon_png}}
|
||||
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
|
||||
{{/if}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
|
||||
{{#if print_enable}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
|
||||
{{/if}}
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
|
||||
{{#if copy_fonts}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
|
||||
{{/if}}
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
{{#each additional_css}}
|
||||
<link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
|
||||
{{/each}}
|
||||
|
||||
{{#if mathjax_support}}
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
{{/if}}
|
||||
</head>
|
||||
<body class="sidebar-visible no-js">
|
||||
<div id="body-container">
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "{{ path_to_root }}";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('{{ default_theme }}')
|
||||
html.classList.add(theme);
|
||||
var body = document.querySelector('body');
|
||||
body.classList.remove('no-js')
|
||||
body.classList.add('js');
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var body = document.querySelector('body');
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
body.classList.remove('sidebar-visible');
|
||||
body.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
{{#toc}}{{/toc}}
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Track and set sidebar scroll position -->
|
||||
<script>
|
||||
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
||||
sidebarScrollbox.addEventListener('click', function(e) {
|
||||
if (e.target.tagName === 'A') {
|
||||
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
||||
}
|
||||
}, { passive: true });
|
||||
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
||||
sessionStorage.removeItem('sidebar-scroll');
|
||||
if (sidebarScrollTop) {
|
||||
// preserve sidebar scroll position when navigating via links within sidebar
|
||||
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
||||
} else {
|
||||
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
||||
var activeSection = document.querySelector('#sidebar .active');
|
||||
if (activeSection) {
|
||||
activeSection.scrollIntoView({ block: 'center' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
{{> header}}
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
|
||||
<!-- TODO remove weird padding here -->
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Dark</button></li>
|
||||
</ul>
|
||||
|
||||
<!-- CUSTOM -->
|
||||
<!-- TODO style when open -->
|
||||
<select id="dropdown-menu" class="icon-button">
|
||||
<option value="">SDKS</option>
|
||||
<option value="https://nymtech.net/docs/sdk/rust/rust.html">Rust</option>
|
||||
<option value="https://sdk.nymtech.net">Typescript</option>
|
||||
</select>
|
||||
|
||||
<!-- TODO style when open -->
|
||||
<select id="dropdown-menu2" class="icon-button">
|
||||
<option value="">Doc Sites</option>
|
||||
<option value="https://nymtech.net/developers">Dev Portal</option>
|
||||
<option value="https://nymtech.net/docs/introduction.html">Network Docs</option>
|
||||
<option value="https://https://nymtech.net/operators">Operator Docs</option>
|
||||
</select>
|
||||
|
||||
<script>
|
||||
document.getElementById('dropdown-menu').addEventListener('change', function() {
|
||||
const selected = this.options[this.selectedIndex];
|
||||
if (selected.value !== '') {
|
||||
window.location.href = selected.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.getElementById('dropdown-menu2').addEventListener('change', function() {
|
||||
const selected = this.options[this.selectedIndex];
|
||||
if (selected.value !== '') {
|
||||
window.location.href = selected.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- END CUSTOM -->
|
||||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
{{#if search_enabled}}
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if print_enable}}
|
||||
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if git_repository_url}}
|
||||
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if git_repository_edit_url}}
|
||||
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if search_enabled}}
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<main>
|
||||
<div class="content-wrap">
|
||||
{{{ content }}}
|
||||
</div>
|
||||
<div class="sidetoc">
|
||||
<nav class="pagetoc"></nav>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
{{#if live_reload_endpoint}}
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_line_numbers}}
|
||||
<script>
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_copyable}}
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_js}}
|
||||
<script src="{{ path_to_root }}ace.js"></script>
|
||||
<script src="{{ path_to_root }}editor.js"></script>
|
||||
<script src="{{ path_to_root }}mode-rust.js"></script>
|
||||
<script src="{{ path_to_root }}theme-dawn.js"></script>
|
||||
<script src="{{ path_to_root }}theme-tomorrow_night.js"></script>
|
||||
{{/if}}
|
||||
|
||||
{{#if search_js}}
|
||||
<script src="{{ path_to_root }}elasticlunr.min.js"></script>
|
||||
<script src="{{ path_to_root }}mark.min.js"></script>
|
||||
<script src="{{ path_to_root }}searcher.js"></script>
|
||||
{{/if}}
|
||||
|
||||
<script src="{{ path_to_root }}clipboard.min.js"></script>
|
||||
<script src="{{ path_to_root }}highlight.js"></script>
|
||||
<script src="{{ path_to_root }}book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
{{#each additional_js}}
|
||||
<script src="{{ ../path_to_root }}{{this}}"></script>
|
||||
{{/each}}
|
||||
|
||||
{{#if is_print}}
|
||||
{{#if mathjax_support}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
MathJax.Hub.Register.StartupHook('End', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{else}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
</script>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,101 +0,0 @@
|
||||
:root {
|
||||
--toc-width: 270px;
|
||||
--center-content-toc-shift: calc(-1 * var(--toc-width) / 2);
|
||||
}
|
||||
|
||||
.nav-chapters {
|
||||
/* adjust width of buttons that bring to the previous or the next page */
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.previous {
|
||||
/*
|
||||
adjust the space between the left sidebar or the left side of the screen
|
||||
and the button that leads to the previous page
|
||||
*/
|
||||
margin-left: var(--page-padding);
|
||||
}
|
||||
|
||||
@media only screen {
|
||||
main {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (max-width: 1179px) {
|
||||
.sidebar-hidden .sidetoc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1439px) {
|
||||
.sidebar-visible .sidetoc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (1180px <= width <= 1439px) {
|
||||
.sidebar-hidden main {
|
||||
position: relative;
|
||||
left: var(--center-content-toc-shift);
|
||||
}
|
||||
}
|
||||
|
||||
@media (1440px <= width <= 1700px) {
|
||||
.sidebar-visible main {
|
||||
position: relative;
|
||||
left: var(--center-content-toc-shift);
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrap {
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidetoc {
|
||||
margin-top: 20px;
|
||||
margin-left: 10px;
|
||||
margin-right: auto;
|
||||
}
|
||||
.pagetoc {
|
||||
position: fixed;
|
||||
/* adjust TOC width */
|
||||
width: var(--toc-width);
|
||||
height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
|
||||
overflow: auto;
|
||||
}
|
||||
.pagetoc a {
|
||||
border-left: 1px solid var(--sidebar-bg);
|
||||
color: var(--fg) !important;
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
padding-left: 10px;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
}
|
||||
.pagetoc a:hover,
|
||||
.pagetoc a.active {
|
||||
background: var(--sidebar-bg);
|
||||
color: var(--sidebar-fg) !important;
|
||||
}
|
||||
.pagetoc .active {
|
||||
background: var(--sidebar-bg);
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
.pagetoc .pagetoc-H2 {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.pagetoc .pagetoc-H3 {
|
||||
padding-left: 40px;
|
||||
}
|
||||
.pagetoc .pagetoc-H4 {
|
||||
padding-left: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.sidetoc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
function forEach(elems, fun) {
|
||||
Array.prototype.forEach.call(elems, fun);
|
||||
}
|
||||
|
||||
function getPagetoc(){
|
||||
return document.getElementsByClassName("pagetoc")[0]
|
||||
}
|
||||
|
||||
function getPagetocElems() {
|
||||
return getPagetoc().children;
|
||||
}
|
||||
|
||||
function getHeaders(){
|
||||
return document.getElementsByClassName("header")
|
||||
}
|
||||
|
||||
// Un-active everything when you click it
|
||||
function forPagetocElem(fun) {
|
||||
forEach(getPagetocElems(), fun);
|
||||
}
|
||||
|
||||
function getRect(element) {
|
||||
return element.getBoundingClientRect();
|
||||
}
|
||||
|
||||
function overflowTop(container, element) {
|
||||
return getRect(container).top - getRect(element).top;
|
||||
}
|
||||
|
||||
function overflowBottom(container, element) {
|
||||
return getRect(container).bottom - getRect(element).bottom;
|
||||
}
|
||||
|
||||
var activeHref = location.href;
|
||||
|
||||
var updateFunction = function (elem = undefined) {
|
||||
var id = elem;
|
||||
|
||||
if (!id && location.href != activeHref) {
|
||||
activeHref = location.href;
|
||||
forPagetocElem(function (el) {
|
||||
if (el.href === activeHref) {
|
||||
id = el;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
var elements = getHeaders();
|
||||
let margin = window.innerHeight / 3;
|
||||
|
||||
forEach(elements, function (el, i, arr) {
|
||||
if (!id && getRect(el).top >= 0) {
|
||||
if (getRect(el).top < margin) {
|
||||
id = el;
|
||||
} else {
|
||||
id = arr[Math.max(0, i - 1)];
|
||||
}
|
||||
}
|
||||
// a very long last section
|
||||
// its heading is over the screen
|
||||
if (!id && i == arr.length - 1) {
|
||||
id = el
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
forPagetocElem(function (el) {
|
||||
el.classList.remove("active");
|
||||
});
|
||||
|
||||
if (!id) return;
|
||||
|
||||
forPagetocElem(function (el) {
|
||||
if (id.href.localeCompare(el.href) == 0) {
|
||||
el.classList.add("active");
|
||||
let pagetoc = getPagetoc();
|
||||
if (overflowTop(pagetoc, el) > 0) {
|
||||
pagetoc.scrollTop = el.offsetTop;
|
||||
}
|
||||
if (overflowBottom(pagetoc, el) < 0) {
|
||||
pagetoc.scrollTop -= overflowBottom(pagetoc, el);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let elements = getHeaders();
|
||||
|
||||
if (elements.length > 1) {
|
||||
// Populate sidebar on load
|
||||
window.addEventListener("load", function () {
|
||||
var pagetoc = getPagetoc();
|
||||
var elements = getHeaders();
|
||||
forEach(elements, function (el) {
|
||||
var link = document.createElement("a");
|
||||
link.appendChild(document.createTextNode(el.text));
|
||||
link.href = el.hash;
|
||||
link.classList.add("pagetoc-" + el.parentElement.tagName);
|
||||
pagetoc.appendChild(link);
|
||||
link.onclick = function () {
|
||||
updateFunction(link);
|
||||
};
|
||||
});
|
||||
updateFunction();
|
||||
});
|
||||
|
||||
// Handle active elements on scroll
|
||||
window.addEventListener("scroll", function () {
|
||||
updateFunction();
|
||||
});
|
||||
} else {
|
||||
document.getElementsByClassName("sidetoc")[0].remove();
|
||||
}
|
||||
@@ -18,16 +18,6 @@
|
||||
|
||||
# User Manuals
|
||||
|
||||
- [NymVPN alpha](nymvpn/intro.md)
|
||||
- [GUI](nymvpn/gui.md)
|
||||
- [Linux](nymvpn/gui-linux.md)
|
||||
- [MacOS](nymvpn/gui-mac.md)
|
||||
- [CLI](nymvpn/cli.md)
|
||||
- [Linux](nymvpn/cli-linux.md)
|
||||
- [MacOS](nymvpn/cli-mac.md)
|
||||
- [Testing](nymvpn/testing.md)
|
||||
- [Troubleshooting](nymvpn/troubleshooting.md)
|
||||
- [NymVPN FAQ](nymvpn/faq.md)
|
||||
- [NymConnect X Monero](tutorials/monero.md)
|
||||
- [NymConnect X Matrix](tutorials/matrix.md)
|
||||
- [NymConnect X Telegram](tutorials/telegram.md)
|
||||
@@ -78,6 +68,12 @@
|
||||
|
||||
# Events
|
||||
|
||||
- [CryptoTalk](events/cryptotalk/nym-vpn.md)
|
||||
- [NymVPN Linux Guide](events/cryptotalk/nym-vpn-linux.md)
|
||||
- [NymVPN MacOS Guide](events/cryptotalk/nym-vpn-mac.md)
|
||||
- [NymVPN Testing](events/cryptotalk/nym-vpn-testing.md)
|
||||
- [NymVPN Troubleshooting](events/cryptotalk/nym-vpn-troubleshooting.md)
|
||||
- [NymVPN FAQ](events/cryptotalk/nym-vpn-faq.md)
|
||||
- [Web3Privacy Now](./events/web3-privacy.md)
|
||||
- [HCPP23-serinko](./events/hcpp23-serinko.md)
|
||||
- [HCPP23-max](./events/hcpp23-max.md)
|
||||
|
||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
@@ -1,14 +1,12 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
**NymVPN [*Support & FAQ page*](https://nymvpn.com/en/support)** contains all essential FAQs regarding the client. This page (below) is a source of additional information often seeked by users, operators and developers testing NymVPN.
|
||||
|
||||
If you interested to read more about Nym platform, you can have a look at [Nym general FAQ](https://nymtech.net/developers/faq/general-faq.html) and read through Nym's technical [documentation](https://nymtech.net/docs), [Developer Portal](https://nymtech.net/developers) and [Operators Guide](https://nymtech.net/operators).
|
||||
This is a FAQ page tailored for this event. If you interested to read more about Nym platform, you can have a look at [Nym general FAQ](https://nymtech.net/developers/faq/general-faq.html) and read through Nym's technical [documentation](https://nymtech.net/docs), [Developer Portal](https://nymtech.net/developers) and [Operators Guide](https://nymtech.net/operators).
|
||||
|
||||
## NymVPN
|
||||
|
||||
If this your first time hearing about NymVPN, make sure you visit [NymVPN webpage](https://nymvpn.com/en), the official NymVPN [support & FAQ page](https://nymvpn.com/en/support) and the proceed to the introduction and guide on how to [install, run and test](intro.md#nymvpn-guides) the client.
|
||||
If this your first time hearing about NymVPN, make sure you visit [NymVPN webpage](https://nymvpn.com/en), the official NymVPN [support & FAQ page](https://nymvpn.com/en/support) and the proceed to the introduction and guide on how to [install, run and test](./nym-vpn.md) the client.
|
||||
|
||||
Below are some extra FAQs which came out during the previous alpha testing rounds.
|
||||
Below are some extra FAQs which came out during the initial alpha testing round.
|
||||
|
||||
### What's the difference between 2-hops and 5-hops
|
||||
|
||||
@@ -56,11 +54,11 @@ Project Smoosh is a code name for a process in which different components of Nym
|
||||
|
||||
## Exit Gateway
|
||||
|
||||
Part of the the transition under code name [Project Smoosh](#project-smoosh) is a creation of [Nym Exit Gateway](https://nymtech.net/operators/legal/exit-gateway.html) functionality. The operators running Gateways would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays. The main change will be to expand the original short [allowed.list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a more permissive setup. An [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) will constrain the hosts that the users of the Nym VPN and Mixnet can connect to. This will be done in an effort to protect the operators, as Gateways will act both as SOCKS5 Network Requesters, and exit nodes for IP traffic from Nym VPN and Mixnet clients.
|
||||
Part of the the transition under code name [Project Smoosh](./nym-vpn-faq.md#project-smoosh) is a creation of [Nym Exit Gateway](https://nymtech.net/operators/legal/exit-gateway.html) functionality. The operators running Gateways would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays. The main change will be to expand the original short [allowed.list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a more permissive setup. An [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) will constrain the hosts that the users of the Nym VPN and Mixnet can connect to. This will be done in an effort to protect the operators, as Gateways will act both as SOCKS5 Network Requesters, and exit nodes for IP traffic from Nym VPN and Mixnet clients.
|
||||
|
||||
* Read more how the exit policy gets implemented [here](https://nymtech.net/operators/faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented)
|
||||
* Check out [Nym Operators Legal Forum](https://nymtech.net/operators/legal/exit-gateway.html)
|
||||
* Do reach out to us with any experiences you may have running Tor Exit relays or legal findings and suggestions for Nym Exit Gateway operators
|
||||
* Do reach out to us during 37c3 with any experiences you may have running Tor Exit relays or legal findings and suggestions for Nym Exit Gateway operators
|
||||
|
||||
## Nym Integrations and SDKs
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
# NymVPN alpha: Guide for GNU/Linux
|
||||
|
||||
```admonish warning
|
||||
NymVPN is an experimental software and it's for [testing](./nym-vpn-testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the event), and follow the steps listed in the form [*NymVPN User research*](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2).
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
You can use our automated script of follow the steps below to download, verify, install and run NymVPN client.
|
||||
|
||||
|
||||
### Automated Script CLI Installation
|
||||
|
||||
Open a terminal and follow these steps:
|
||||
|
||||
* Download the script
|
||||
```sh
|
||||
curl -o nym-vpn-cli-executor.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/99cea8f4d80f2d002802ed1cbedba288bfca4488/execute-nym-vpn-cli-binary.sh
|
||||
```
|
||||
* Make the script executable
|
||||
```sh
|
||||
chmod u+x nym-vpn-cli-executor.sh
|
||||
```
|
||||
* Run the script
|
||||
```sh
|
||||
./nym-vpn-cli-executor.sh
|
||||
```
|
||||
* When prompted to verify `sha256sum` paste one from the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) including the binary name (all as one input with a space in between), for example:
|
||||
```sh
|
||||
61a08de46881411e140f66cdb4940041150adb573c10e3f510a58a86a32f08fb nym-vpn-cli-ubuntu-22.04.zip
|
||||
```
|
||||
|
||||
The script will automatically start the client. Follow the instructions:
|
||||
|
||||
* The script will print a JSON view of existing Gateways and prompt you to:
|
||||
- ***(Make sure to use two different Gateways for entry and exit!)***
|
||||
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
|
||||
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
|
||||
- `do you want five hop or two hop?`: type `five` or `two`
|
||||
|
||||
### Manual CLI and GUI Installation
|
||||
|
||||
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) to download the binary for Debian based Linux
|
||||
* Open terminal in the same directory and check the the `sha256sum` by running:
|
||||
```sh
|
||||
# for CLI
|
||||
sha256sum ./nym-vpn-cli_0.1.0_ubuntu-22.04_amd64.zip
|
||||
|
||||
# for GUI
|
||||
sha256sum ./nym-vpn-ui_0.0.2_ubuntu-22.04_amd64.zip
|
||||
```
|
||||
* Compare the result with the sha256 hash shared on the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2)
|
||||
* Extract files with `unzip` command or manually as you are used to
|
||||
* If you prefer to run `.AppImage` make executable by running:
|
||||
```sh
|
||||
# for CLI
|
||||
chmod +x ./nym-vpn-cli
|
||||
|
||||
# for GUI
|
||||
chmod +x ./appimage/nym-vpn_0.0.2_amd64.AppImage
|
||||
# make sure your path to package is correct and the package name as well
|
||||
```
|
||||
* If you prefer to use the `.deb` version for installation (Linux only), open terminal in the same directory and run:
|
||||
```sh
|
||||
cd deb
|
||||
|
||||
sudo dpkg -i ./nym-vpn_0.0.2_amd64.deb
|
||||
|
||||
# or
|
||||
sudo apt-get install -f ./nym-vpn_0.0.2_amd64.deb
|
||||
|
||||
```
|
||||
* **For CLI**: Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries. In case of GUI setup, see the steps below.
|
||||
|
||||
### GUI configuration
|
||||
|
||||
* Create a NymVPN config directory called `nym-vpn` in your `~/.config`, either manually or by a command:
|
||||
```sh
|
||||
# for Linux
|
||||
mkdir $HOME/.config/nym-vpn/
|
||||
```
|
||||
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the directory `nym-vpn` you just created
|
||||
* Create the main config file called `config.toml` in the same directory with this content:
|
||||
```toml
|
||||
# change <USER> to your username
|
||||
env_config_file = "/home/<USER>/.config/nym-vpn/sandbox.env"
|
||||
entry_node_location = "DE" # two letters country code
|
||||
```
|
||||
|
||||
## Run NymVPN
|
||||
|
||||
* **For NymVPN to work, all other VPNs must be switched off!**
|
||||
* At this alpha stage of NymVPN, network connection (wifi) must be re-connected after or in-between NymVPN testing rounds.
|
||||
|
||||
### Run CLI
|
||||
|
||||
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
|
||||
|
||||
* Find the entire command with all the needed arguments' values and your wireguard private key for testing purposes at [nymvpn.com/en/alpha](https://nymvpn.com/en/alpha)
|
||||
* Run it with `sudo` as root, the command will look like this with specified arguments:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
|
||||
```
|
||||
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
|
||||
* To see all possibilities see [command explanation](#cli-commands-and-options) below
|
||||
|
||||
### Run GUI
|
||||
|
||||
In case you used `.deb` package and installed the client, you may be able to have a NymVPN application icon in your app menu. However this may not work as the application needs root permission.
|
||||
|
||||
Make sure you went through the GUI configuration in the [preparation section](#gui-configuration). Then open terminal in the same directory where you [installed](#preparation) the binary and run:
|
||||
|
||||
```sh
|
||||
# .AppImage must be run from the same directory
|
||||
sudo -E ./nym-vpn_0.0.2_amd64.AppImage
|
||||
|
||||
# .deb installation shall be executable from anywhere as
|
||||
sudo -E nym-vpn
|
||||
```
|
||||
|
||||
In case of errors, see [troubleshooting section](./nym-vpn-troubleshooting.md).
|
||||
|
||||
### CLI Commands and Options
|
||||
|
||||
The basic syntax of `nym-vpn-cli` is:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
|
||||
```
|
||||
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
|
||||
* To see all possibilities run with `--help` flag:
|
||||
```sh
|
||||
./nym-vpn-cli --help
|
||||
```
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
Usage: nym-vpn-cli [OPTIONS]
|
||||
|
||||
Options:
|
||||
-c, --config-env-file <CONFIG_ENV_FILE>
|
||||
Path pointing to an env file describing the network
|
||||
--mixnet-client-path <MIXNET_CLIENT_PATH>
|
||||
Path to the data directory of a previously initialised mixnet client, where the keys reside
|
||||
--entry-gateway-id <ENTRY_GATEWAY_ID>
|
||||
Mixnet public ID of the entry gateway
|
||||
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
|
||||
Auto-select entry gateway by country ISO
|
||||
--exit-router-address <EXIT_ROUTER_ADDRESS>
|
||||
Mixnet recipient address
|
||||
--exit-gateway-id <EXIT_GATEWAY_ID>
|
||||
|
||||
--exit-router-country <EXIT_ROUTER_COUNTRY>
|
||||
Mixnet recipient address
|
||||
--enable-wireguard
|
||||
Enable the wireguard traffic between the client and the entry gateway
|
||||
--private-key <PRIVATE_KEY>
|
||||
Associated private key
|
||||
--wg-ip <WG_IP>
|
||||
The IP address of the wireguard interface
|
||||
--ip <IP>
|
||||
The IP address of the TUN device
|
||||
--mtu <MTU>
|
||||
The MTU of the TUN device
|
||||
--disable-routing
|
||||
Disable routing all traffic through the VPN TUN device
|
||||
--enable-two-hop
|
||||
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
|
||||
--enable-poisson-rate
|
||||
Enable Poisson process rate limiting of outbound traffic
|
||||
-h, --help
|
||||
Print help
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
**Fundamental commands and arguments**
|
||||
|
||||
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to adjusted by users:
|
||||
|
||||
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
|
||||
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
|
||||
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
|
||||
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
|
||||
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
|
||||
|
||||
**Advanced options**
|
||||
|
||||
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
|
||||
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
|
||||
@@ -0,0 +1,191 @@
|
||||
# NymVPN alpha: Guide for Mac OS
|
||||
|
||||
```admonish warning
|
||||
NymVPN is an experimental software and it's for [testing](./nym-vpn-testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the event), and follow the steps listed in the form [*NymVPN User research*](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2).
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
### CLI Installation
|
||||
|
||||
You can use our automated script of follow the steps below to download, verify, install and run NymVPN client.
|
||||
|
||||
#### Automated Script CLI Installation
|
||||
|
||||
Open a terminal and follow these steps:
|
||||
|
||||
* Download the script
|
||||
```sh
|
||||
curl -o nym-vpn-client-executor.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/99cea8f4d80f2d002802ed1cbedba288bfca4488/execute-nym-vpn-cli-binary.sh
|
||||
```
|
||||
* Make the script executable
|
||||
```sh
|
||||
chmod u+x nym-vpn-client-executor.sh
|
||||
```
|
||||
* Run the script
|
||||
```sh
|
||||
./nym-vpn-client-executor.sh
|
||||
```
|
||||
* When prompted to verify `sha256sum` paste one from the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) including the binary name (all as one input with a space in between), for example:
|
||||
```sh
|
||||
96623ccc69bc4cc0e4e3e18528b6dae6be69f645d0a592d926a3158ce2d0c269 nym-vpn-cli_0.1.0_macos_x86_64.zip
|
||||
```
|
||||
|
||||
The script will automatically start the client. Follow the instructions:
|
||||
|
||||
* The script will print a JSON view of existing Gateways and prompt you to:
|
||||
- ***(Make sure to use two different Gateways for entry and exit!)***
|
||||
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
|
||||
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
|
||||
- `do you want five hop or two hop?`: type `five` or `two`
|
||||
|
||||
#### Manual CLI Installation
|
||||
|
||||
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) to download the binary for Debian based Linux
|
||||
* Open terminal in the same directory and check the the `sha256sum` by running:
|
||||
```sh
|
||||
# x86_64
|
||||
sha256sum ./nym-vpn-cli_0.1.0_macos_x86_64.zip
|
||||
|
||||
# aarch64
|
||||
sha256sum ./nym-vpn-cli_0.1.0_macos_aarch64.zip
|
||||
```
|
||||
* Compare the result with the sha256 hash shared on the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2)
|
||||
* Extract files with `unzip` command or manually as you are used to
|
||||
```sh
|
||||
# for CLI
|
||||
chmod +x ./nym-vpn-cli
|
||||
```
|
||||
|
||||
* Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries. In case of GUI setup, see the steps below.
|
||||
|
||||
### GUI Installation
|
||||
|
||||
We created a [script](https://gist.github.com/tommyv1987/7d210d4daa8f7abc61f9a696d0321f19) which does download, extraction, installation and configuration for MacOS users automatically following the steps below:
|
||||
|
||||
* To download the script, open a terminal in a directory where you want to download the script and run:
|
||||
```sh
|
||||
curl -o nym-vpn-client-executor.sh - L https://gist.githubusercontent.com/tommyv1987/7d210d4daa8f7abc61f9a696d0321f19/raw/4397365b4cf74594c7f99c1ef5d690b2f5b41192/nym-vpn-client-macos-executor.sh
|
||||
```
|
||||
* Make executable
|
||||
```sh
|
||||
chmod u+x nym-vpn-client-macos-executor.sh
|
||||
```
|
||||
* Run
|
||||
```sh
|
||||
./nym-vpn-client-macos-executor.sh
|
||||
```
|
||||
* When prompted to verify `sha256sum` paste one from the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) including the binary name (all as one input with a space in between), for example:
|
||||
```sh
|
||||
06c7c82f032f230187da1002a9a9a88242d3bbf6c5c09dc961a71df151d768d0 nym-vpn-ui_0.0.2_macos_x86_64.zip
|
||||
```
|
||||
* The script will run the application and it will prompt you for a country code to exit, chose one of the offered options
|
||||
|
||||
In case of errors check out the [troubleshooting](./nym-vpn-troubleshooting.html#installing-gui-on-macos-not-working) section.
|
||||
|
||||
## Run NymVPN
|
||||
|
||||
* **For NymVPN to work, all other VPNs must be switched off!**
|
||||
* At this alpha stage of NymVPN, network connection (wifi) must be re-connected after or in-between NymVPN testing rounds.
|
||||
|
||||
### Run CLI
|
||||
|
||||
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
|
||||
|
||||
* Find the entire command with all the needed arguments' values and your wireguard private key for testing purposes at [nymvpn.com/en/alpha](https://nymvpn.com/en/alpha)
|
||||
* Run it with `sudo` as root, the command will look like this with specified arguments:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
|
||||
```
|
||||
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
|
||||
* To see all possibilities see [command explanation](#cli-commands-and-options) below
|
||||
|
||||
### Run GUI
|
||||
|
||||
Make sure you went through the GUI configuration in the [preparation section](#gui-installation).
|
||||
|
||||
You may be able to have a NymVPN application icon in your app menu. However this may not work as the application needs root permission.
|
||||
|
||||
* Run GUI from terminal:
|
||||
```sh
|
||||
sudo $nym_vpn_dir/nym-vpn
|
||||
```
|
||||
|
||||
In case of errors, see [troubleshooting section](./nym-vpn-troubleshooting.md#macos-alert-on-nymvpn-ui-startup).
|
||||
|
||||
### CLI Commands and Options
|
||||
|
||||
The basic syntax of `nym-vpn-cli` is:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
|
||||
```
|
||||
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
|
||||
* To see all possibilities run with `--help` flag:
|
||||
```sh
|
||||
./nym-vpn-cli --help
|
||||
```
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
Usage: nym-vpn-cli [OPTIONS]
|
||||
|
||||
Options:
|
||||
-c, --config-env-file <CONFIG_ENV_FILE>
|
||||
Path pointing to an env file describing the network
|
||||
--mixnet-client-path <MIXNET_CLIENT_PATH>
|
||||
Path to the data directory of a previously initialised mixnet client, where the keys reside
|
||||
--entry-gateway-id <ENTRY_GATEWAY_ID>
|
||||
Mixnet public ID of the entry gateway
|
||||
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
|
||||
Auto-select entry gateway by country ISO
|
||||
--exit-router-address <EXIT_ROUTER_ADDRESS>
|
||||
Mixnet recipient address
|
||||
--exit-gateway-id <EXIT_GATEWAY_ID>
|
||||
|
||||
--exit-router-country <EXIT_ROUTER_COUNTRY>
|
||||
Mixnet recipient address
|
||||
--enable-wireguard
|
||||
Enable the wireguard traffic between the client and the entry gateway
|
||||
--private-key <PRIVATE_KEY>
|
||||
Associated private key
|
||||
--wg-ip <WG_IP>
|
||||
The IP address of the wireguard interface
|
||||
--ip <IP>
|
||||
The IP address of the TUN device
|
||||
--mtu <MTU>
|
||||
The MTU of the TUN device
|
||||
--disable-routing
|
||||
Disable routing all traffic through the VPN TUN device
|
||||
--enable-two-hop
|
||||
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
|
||||
--enable-poisson-rate
|
||||
Enable Poisson process rate limiting of outbound traffic
|
||||
-h, --help
|
||||
Print help
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
**Fundamental commands and arguments**
|
||||
|
||||
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to adjusted by users:
|
||||
|
||||
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
|
||||
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
|
||||
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
|
||||
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
|
||||
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
|
||||
|
||||
**Advanced options**
|
||||
|
||||
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
|
||||
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
|
||||
@@ -1,12 +1,6 @@
|
||||
# Testing NymVPN alpha
|
||||
|
||||
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/908640440?h=0f7f6dfa53" style="position:absolute;top:0;left:0;width:100%;height:100%;" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
|
||||
|
||||
```admonish info
|
||||
NymVPN is an experimental software and it's for [testing](./testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
|
||||
```
|
||||
|
||||
> Before you get into testing NymVPN, make sure to go through the preparation steps for NymVPN [CLI](cli.md).
|
||||
> Before you get into testing NymVPN, make sure to go through the preparation steps for [Linux](nym-vpn-linux.md) or [MacOS](nym-vpn-mac.md).
|
||||
|
||||
One of the main aims of NymVPN alpha release is testing; your results will help us to make NymVPN robust and stabilise both the client and the network through provided measurements.
|
||||
|
||||
@@ -14,20 +8,14 @@ One of the main aims of NymVPN alpha release is testing; your results will help
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary ([download here]({{nym_vpn_latest_binary_url}})) and [`sandbox.env`](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) to that directory
|
||||
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary and [`sandbox.env`](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) to that directory
|
||||
2. Copy the [block below](#testssh) and save it as `tests.sh` to the same folder
|
||||
3. Open terminal in the same directory
|
||||
4. Turn off any existing VPN's (including the NymVPN instances), reconnect your wifi and make the script executable by running
|
||||
```sh
|
||||
chmod u+x ./tests.sh
|
||||
```
|
||||
5. Run the `tests.sh` script:
|
||||
```sh
|
||||
sudo ./tests.sh
|
||||
````
|
||||
6. In case of errors, see the [troubleshooting section](troubleshooting.md#missing-jq-error)
|
||||
4. Turn off any existing VPN's (including the NymVPN instances), reconnect your wifi and make the script executable by running `chmod +x ./tests.sh`
|
||||
5. Run the tests: `sudo ./tests.sh`
|
||||
6. In case of errors, see the [troubleshooting section](./nym-vpn-troubleshooting.md#missing-jq-error) below
|
||||
7. The script will print a JSON view of existing Gateways and prompt you to:
|
||||
- *Make sure to use two different Gateways for entry and exit!*
|
||||
- ***(Make sure to use two different Gateways for entry and exit!)***
|
||||
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
|
||||
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
|
||||
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
|
||||
@@ -46,8 +34,9 @@ nym-vpn-tests
|
||||
├── timeout
|
||||
└── two_hop_perf_test_results.log
|
||||
```
|
||||
10. When the tests are finished, remove the `nym-vpn-cli` binary from the folder and compress the entire folder as `nym-vpn-tests.zip`
|
||||
11. Upload this compressed file to the [form]({{nym_vpn_form_url}}) upload field when prompted
|
||||
10. In case of errors, see [troubleshooting section](./nym-vpn-troubleshooting.md#missing-jq-error) below
|
||||
11. When the tests are finished, remove the `nym-vpn-cli` binary from the folder and compress the entire folder as `nym-vpn-tests.zip`
|
||||
12. Upload this compressed file to the [questionnaire](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2) upload field when prompted
|
||||
|
||||
#### tests.sh
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Troubleshooting
|
||||
|
||||
Below are listed some points which may need to be addressed when testing NymVPN alpha. If you crashed into any errors which are not listed, please contact us at the event.
|
||||
|
||||
#### Problems with the newest version `nym-vpn-alpha-0.0.2 demo`
|
||||
|
||||
Try the previous version `nym-vpn-alpha-0.0.1 demo` which was tested multiple times by downloading the client from this [release page(https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.1) and doing all the steps with the name of your downloaded binary.
|
||||
|
||||
#### Installing GUI on MacOS not working
|
||||
|
||||
In case there was a problem running the script, try a manual configuration:
|
||||
|
||||
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.1) to download the binary for Debian based Linux
|
||||
* Open terminal in the same directory and check the the `sha256sum` by running:
|
||||
```sh
|
||||
# for MacOS GUI
|
||||
sha256sum ./nym-vpn-ui-macos-latest.zip
|
||||
```
|
||||
* Compare the result with the sha256 hash shared on the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.1)
|
||||
* Extract files with `unzip` command or manually as you are used toi
|
||||
* Create a NymVPN config directory called `nym-vpn` in your `~/.config`, either manually or by a command:
|
||||
```sh
|
||||
mkdir $HOME/Library/Application Support/nym-vpn/
|
||||
```
|
||||
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the config directory `nym-vpn` you just created
|
||||
* Create the main config file called `config.toml` in the same directory with this content:
|
||||
```toml
|
||||
env_config_file = "$HOME/Library/Application Support/nym-vpn/"
|
||||
entry_node_location = "DE" # two letters country code
|
||||
```
|
||||
|
||||
**Note:** Some users had a problem to access their home config folder on macOS, in that case save the configuration files in the same directory where you downloaded your `nym-vpn` binary as following:
|
||||
|
||||
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `.env` (yes just like that) in the same directory like `nym-vpn` binaries.
|
||||
* Create the main config file called `config.toml` in the very same directory with this content:
|
||||
```toml
|
||||
env_config_file = "$HOME/Library/Application Support/nym-vpn/"
|
||||
entry_node_location = "DE" # two letters country code
|
||||
```
|
||||
|
||||
#### Thread `main` panicked
|
||||
|
||||
If you see a message like:
|
||||
```sh
|
||||
thread 'main' panicked at /Users/runner/.cargo/git/checkouts/mullvadvpn-app-a575cf705b5dfd76/ccfbaa2/talpid-routing/src/unix.rs:301:30:
|
||||
```
|
||||
Restart your wifi connection and start again.
|
||||
|
||||
#### macOS alert on NymVPN UI startup
|
||||
|
||||
If you are running NymVPN on mac OS for the first time, you may see this alert message:
|
||||
|
||||

|
||||
|
||||
1. Head to System Settings -> Privacy & Security and click `Allow anyway`
|
||||
|
||||

|
||||
|
||||
2. Confirm with your password or TouchID
|
||||
|
||||
3. Possibly you may have to confirm again upon running the application
|
||||
|
||||
#### Missing `jq` error
|
||||
|
||||
In case of missing `jq` on Linux (Debian) install it with:
|
||||
```sh
|
||||
# Linux (Debian)
|
||||
sudo apt-get install jq
|
||||
# macOS
|
||||
brew install jq
|
||||
```
|
||||
On some Linux distributions however the [script](./nym-vpn.md#testssh) returns `jq` error even if your system claims that `jq is already the newest version`.
|
||||
In that case, comment the `jq` check in the script as follows:
|
||||
```sh
|
||||
#if ! command -v jq &>/dev/null; then
|
||||
# echo "jq is not installed. Please install jq to proceed."
|
||||
# exit 1
|
||||
#fi
|
||||
```
|
||||
|
||||
#### Error current_time: not found
|
||||
|
||||
When running `sudo sh ./test.sh` you may see an error like: `93: current_time: not found`. This has something to do with the `current_time` setup of your system and on itself shall not have a negative impact on the test. It has nothing to do with the client at all as it only relates to the code in our testing script.
|
||||
|
||||
#### Not connecting to the endpoint
|
||||
|
||||
In case the automatic download of all the Gateways fail (and it shouldn't), you do an easy manual work around:
|
||||
|
||||
1. Open the list of Gateways created by API [here](https://nymvpn.com/en/ccc/api/gateways)
|
||||
2. On top click on `JSON` option (shall be default view) and `Save`
|
||||
3. Save it as `data.json` to the `nym-vpn-tests` folder
|
||||
4. Replace line 3 in the [script `tests.sh`](./nym-vpn.md#testssh) with:
|
||||
```sh
|
||||
NEW_ENDPOINT="http://localhost:8000/data.json"
|
||||
```
|
||||
5. In a new terminal window run:
|
||||
```sh
|
||||
python3 -m http.server 8000
|
||||
```
|
||||
6. Continue with the steps listed in [testing section](./nym-vpn.md#testing)
|
||||
@@ -0,0 +1,51 @@
|
||||
# NymVPN alpha
|
||||
|
||||
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/897010658?h=1f55870fe6&badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="NYMVPN alpha demo 37C3"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
|
||||
|
||||
We are honored to present NymVPN, a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features). At this event we have the unique opportunity to be part of the initial alpha testing. The following pages provide a how-to guide, explaining steps to install and run NymVPN CLI and GUI on our testing environment Nym Sandbox.
|
||||
|
||||
## NymVPN
|
||||
|
||||
The following overview provides an easy introduction to the NymVPN alpha client. We recommend interested developers to begin with [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html) pages.
|
||||
|
||||
The default is to run in 5-hop mode:
|
||||
|
||||
```
|
||||
┌─►mix──┐ mix mix
|
||||
│ │
|
||||
Entry │ │ Exit
|
||||
client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet
|
||||
│ │
|
||||
│ │
|
||||
mix └─►mix──┘ mix
|
||||
```
|
||||
|
||||
Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateways, and is not passed between Mix Nodes.
|
||||
|
||||
The client can optionally do the first hop (local client to Entry Gateway) using Wireguard. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device.
|
||||
|
||||
## NymVPN Guide & FAQ pages
|
||||
|
||||
To download, install and test NymVPN alpha, visit pages listed below:
|
||||
|
||||
* [GNU/Linux](nym-vpn-linux.md)
|
||||
* [Mac OS](nym-vpn-mac.md)
|
||||
* [Testing scripts](nym-vpn-testing.md)
|
||||
* [Troubleshooting](nym-vpn-troubleshooting.md)
|
||||
* [NymVPN FAQ](nym-vpn-faq.md)
|
||||
|
||||
## Goals of testing
|
||||
|
||||
This alpha testing will help:
|
||||
|
||||
* Stabilise NymVPN client
|
||||
* Understand NymVPN client behavior in various setups (OS, connectivity, etc.)
|
||||
* Stabilize the VPN infrastructure and improve its reliability / speed / features (e.g. IPv6 support)
|
||||
* Load test the network in Sandbox environment and identify / anticipate potential weaknesses
|
||||
|
||||
|
||||
```admonish info
|
||||
Our alpha testing round is done live with some participants at the event. This guide will not work for everyone, as the NymVPN binaries aren't publicly accessible yet. Note that this setup of Nym testnet Sandbox environment is limited event and some of the configurations will not work in the future.
|
||||
|
||||
**If you commit to test NymVPN alpha, please start with the [user research form](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2) where all the steps will be provided**.
|
||||
```
|
||||
@@ -1,116 +0,0 @@
|
||||
# NymVPN alpha CLI: Guide for GNU/Linux
|
||||
|
||||
```admonish info
|
||||
NymVPN is an experimental software and it's for [testing](./testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
1. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for Debian based Linux
|
||||
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
|
||||
```sh
|
||||
echo "<SHA_STRING>" | shasum -a 256 -c
|
||||
|
||||
# choose a correct one according to your binary, this is just an example
|
||||
echo "0e4abb461e86b2c168577e0294112a3bacd3a24bf8565b49783bfebd9b530e23 nym-vpn-cli_0.1.0_ubuntu-22.04_amd64.zip" | shasum -a 256 -c
|
||||
```
|
||||
3. Extract files with `unzip` command or manually as you are used to
|
||||
4. Make executable by running:
|
||||
```sh
|
||||
chmod u+x ./nym-vpn-cli
|
||||
```
|
||||
5. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
|
||||
```sh
|
||||
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
|
||||
```
|
||||
|
||||
## Run NymVPN
|
||||
|
||||
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
|
||||
|
||||
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
|
||||
|
||||
1. Go to [nymvpn.com/en/alpha](https://nymvpn.com/en/alpha) to get the entire command with all the needed arguments' values and your wireguard private key for testing purposes
|
||||
2. Run it as root with `sudo` - the command will look like this with specified arguments:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WIREGUARD_IP>
|
||||
```
|
||||
3. To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways) and pick one
|
||||
4. See all possibilities in [command explanation](#cli-commands-and-options) section below
|
||||
|
||||
In case of errors, see [troubleshooting section](troubleshooting.md).
|
||||
|
||||
### CLI Commands and Options
|
||||
|
||||
The basic syntax of `nym-vpn-cli` is:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
|
||||
```
|
||||
* To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
|
||||
* To see all possibilities run with `--help` flag:
|
||||
```sh
|
||||
./nym-vpn-cli --help
|
||||
```
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
Usage: nym-vpn-cli [OPTIONS]
|
||||
|
||||
Options:
|
||||
-c, --config-env-file <CONFIG_ENV_FILE>
|
||||
Path pointing to an env file describing the network
|
||||
--mixnet-client-path <MIXNET_CLIENT_PATH>
|
||||
Path to the data directory of a previously initialised mixnet client, where the keys reside
|
||||
--entry-gateway-id <ENTRY_GATEWAY_ID>
|
||||
Mixnet public ID of the entry gateway
|
||||
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
|
||||
Auto-select entry gateway by country ISO
|
||||
--exit-router-address <EXIT_ROUTER_ADDRESS>
|
||||
Mixnet recipient address
|
||||
--exit-gateway-id <EXIT_GATEWAY_ID>
|
||||
|
||||
--exit-router-country <EXIT_ROUTER_COUNTRY>
|
||||
Mixnet recipient address
|
||||
--enable-wireguard
|
||||
Enable the wireguard traffic between the client and the entry gateway
|
||||
--private-key <PRIVATE_KEY>
|
||||
Associated private key
|
||||
--wg-ip <WG_IP>
|
||||
The IP address of the wireguard interface used for the first hop to the entry gateway
|
||||
--nym-ip <NYM_IP>
|
||||
The IP address of the nym TUN device that wraps IP packets in sphinx packets
|
||||
--nym-mtu <NYM_MTU>
|
||||
The MTU of the nym TUN device that wraps IP packets in sphinx packets
|
||||
--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 mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
|
||||
--enable-poisson-rate
|
||||
Enable Poisson process rate limiting of outbound traffic
|
||||
-h, --help
|
||||
Print help
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to be adjusted by users.
|
||||
|
||||
**Fundamental commands and arguments**
|
||||
|
||||
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
|
||||
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
|
||||
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
|
||||
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
|
||||
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
|
||||
|
||||
**Advanced options**
|
||||
|
||||
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
|
||||
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
|
||||
@@ -1,117 +0,0 @@
|
||||
# NymVPN alpha CLI: Guide for MacOS
|
||||
|
||||
```admonish info
|
||||
NymVPN is an experimental software and it's for [testing](./testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
1. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for MacOS
|
||||
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
|
||||
```sh
|
||||
echo "<SHA_STRING>" | shasum -a 256 -c
|
||||
|
||||
# choose a correct one according to your binary, this is just an example
|
||||
echo "96623ccc69bc4cc0e4e3e18528b6dae6be69f645d0a592d926a3158ce2d0c269 nym-vpn-cli_0.1.0_macos_x86_64.zip" | shasum -a 256 -c
|
||||
```
|
||||
5. Extract files with `unzip` command or manually as you are used to
|
||||
6. Make executable by running:
|
||||
```sh
|
||||
chmod u+x ./nym-vpn-cli
|
||||
```
|
||||
7. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
|
||||
```sh
|
||||
curl -L "https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env" -o sandbox.env
|
||||
```
|
||||
|
||||
## Run NymVPN
|
||||
|
||||
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
|
||||
|
||||
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
|
||||
|
||||
|
||||
1. Go to [nymvpn.com/en/alpha](https://nymvpn.com/en/alpha) to get the entire command with all the needed arguments' values and your wireguard private key for testing purposes
|
||||
2. Run it as root with `sudo` - the command will look like this with specified arguments:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WIREGUARD_IP>
|
||||
```
|
||||
3. To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways) and pick one
|
||||
4. See all possibilities in [command explanation](#cli-commands-and-options) section below
|
||||
|
||||
In case of errors, see [troubleshooting section](troubleshooting.md).
|
||||
|
||||
### CLI Commands and Options
|
||||
|
||||
The basic syntax of `nym-vpn-cli` is:
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
|
||||
```
|
||||
* To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
|
||||
* To see all possibilities run with `--help` flag:
|
||||
```sh
|
||||
./nym-vpn-cli --help
|
||||
```
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
Usage: nym-vpn-cli [OPTIONS]
|
||||
|
||||
Options:
|
||||
-c, --config-env-file <CONFIG_ENV_FILE>
|
||||
Path pointing to an env file describing the network
|
||||
--mixnet-client-path <MIXNET_CLIENT_PATH>
|
||||
Path to the data directory of a previously initialised mixnet client, where the keys reside
|
||||
--entry-gateway-id <ENTRY_GATEWAY_ID>
|
||||
Mixnet public ID of the entry gateway
|
||||
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
|
||||
Auto-select entry gateway by country ISO
|
||||
--exit-router-address <EXIT_ROUTER_ADDRESS>
|
||||
Mixnet recipient address
|
||||
--exit-gateway-id <EXIT_GATEWAY_ID>
|
||||
|
||||
--exit-router-country <EXIT_ROUTER_COUNTRY>
|
||||
Mixnet recipient address
|
||||
--enable-wireguard
|
||||
Enable the wireguard traffic between the client and the entry gateway
|
||||
--private-key <PRIVATE_KEY>
|
||||
Associated private key
|
||||
--wg-ip <WG_IP>
|
||||
The IP address of the wireguard interface used for the first hop to the entry gateway
|
||||
--nym-ip <NYM_IP>
|
||||
The IP address of the nym TUN device that wraps IP packets in sphinx packets
|
||||
--nym-mtu <NYM_MTU>
|
||||
The MTU of the nym TUN device that wraps IP packets in sphinx packets
|
||||
--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 mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
|
||||
--enable-poisson-rate
|
||||
Enable Poisson process rate limiting of outbound traffic
|
||||
-h, --help
|
||||
Print help
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to be adjusted by users.
|
||||
|
||||
**Fundamental commands and arguments**
|
||||
|
||||
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
|
||||
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/alpha/api/gateways)
|
||||
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
|
||||
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
|
||||
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
|
||||
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
|
||||
|
||||
**Advanced options**
|
||||
|
||||
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
|
||||
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
|
||||
@@ -1,46 +0,0 @@
|
||||
# NymVPN Command Line Interface (CLI)
|
||||
|
||||
```admonish info
|
||||
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
|
||||
|
||||
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
|
||||
```
|
||||
|
||||
NymVPN CLI is a fundamental way to run the client for different purposes, currently it is a must for users who want to run the [testing scripts](testing.md).
|
||||
|
||||
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
|
||||
|
||||
## Automated Script for CLI Installation
|
||||
|
||||
We wrote a [script](https://gist.github.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
|
||||
|
||||
1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
|
||||
```sh
|
||||
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/2a8b703655549f2e515ef1960b5f6dc54adc02fa/execute-nym-vpn-cli-binary.sh
|
||||
```
|
||||
|
||||
2. Make the script executable
|
||||
```sh
|
||||
chmod u+x execute-nym-vpn-cli-binary.sh
|
||||
```
|
||||
|
||||
3. Start the script as root, turn off any VPN and run
|
||||
```sh
|
||||
sudo ./execute-nym-vpn-cli-binary.sh
|
||||
```
|
||||
|
||||
4. Follow the prompts in the program
|
||||
|
||||
5. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
|
||||
|
||||
* It prints a JSON view of existing Gateways and prompt you to:
|
||||
- *Make sure to use two different Gateways for entry and exit!*
|
||||
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
|
||||
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
|
||||
- `do you want five hop or two hop?`: type `five` or `two`
|
||||
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
|
||||
|
||||
To run `nym-vpn-cli` again, reconnect your wifi, move to the directory of your CLI binary `cd ~/nym-vpn-cli-dir` and follow the guide for [Linux](cli-linux.md#run-nymvpn) or [MacOS](cli-mac.md#run-nymvpn). If you find it too difficult, just run this script again - like in step \#3 above.
|
||||
|
||||
In case of errors check out the [troubleshooting](troubleshooting.md) section.
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# NymVPN alpha GUI: Guide for GNU/Linux
|
||||
|
||||
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/908221306?h=404b2bbdc8" style="position:absolute;top:0;left:0;width:100%;height:100%;" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
|
||||
|
||||
```admonish info
|
||||
NymVPN is an experimental software and it's for [testing](./testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
### Installation
|
||||
|
||||
1. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for Debian based Linux
|
||||
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
|
||||
```sh
|
||||
echo "<SHA_STRING>" | shasum -a 256 -c
|
||||
|
||||
# choose a correct one according to your binary, this is just an example
|
||||
echo "a5f91f20d587975e30b6a75d3a9e195234cf1269eac278139a5b9c39b039e807 nym-vpn-desktop_0.0.3_ubuntu-22.04_x86_64.zip" | shasum -a 256 -c
|
||||
```
|
||||
3. Extract files with `unzip` command or manually as you are used to
|
||||
4. If you prefer to run `.AppImage` make executable by running:
|
||||
```sh
|
||||
chmod u+x ./appimage/nym-vpn_0.0.2_amd64.AppImage
|
||||
```
|
||||
5. If you prefer to use the `.deb` version for installation (works on Debian based Linux only), open terminal in the same directory and run:
|
||||
```sh
|
||||
cd deb
|
||||
|
||||
sudo dpkg -i ./nym-vpn_0.0.3_amd64.deb
|
||||
# or
|
||||
sudo apt-get install -f ./nym-vpn_0.0.3_amd64.deb
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
6. Create a NymVPN config directory called `nym-vpn` in your `~/.config`, either manually or by a command:
|
||||
```sh
|
||||
mkdir $HOME/.config/nym-vpn/
|
||||
```
|
||||
7. Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the directory `~/.config/nym-vpn/` you just created by running:
|
||||
```sh
|
||||
curl -o $HOME/.config/nym-vpn/sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
|
||||
```
|
||||
|
||||
8. Create NymVPN main config file called `config.toml` in the same directory `~/.config/nym-vpn/` with this content:
|
||||
```toml
|
||||
# change <USER> to your username
|
||||
env_config_file = "/home/<USER>/.config/nym-vpn/sandbox.env"
|
||||
entry_node_location = "DE" # two letters country code
|
||||
# You can choose different entry by entering one of the following two letter country codes:
|
||||
# DE, UK, FR, IE
|
||||
```
|
||||
|
||||
## Run NymVPN
|
||||
|
||||
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
|
||||
|
||||
In case you used `.deb` package and installed the client, you may be able to have a NymVPN application icon in your app menu. However this may not work as the application needs root permission.
|
||||
|
||||
Open terminal and run:
|
||||
|
||||
```sh
|
||||
# .AppImage must be run from the same directory as the binary
|
||||
sudo -E ./nym-vpn_0.0.2_amd64.AppImage
|
||||
|
||||
# .deb installation shall be executable from anywhere as
|
||||
sudo -E nym-vpn
|
||||
```
|
||||
|
||||
In case of errors, see [troubleshooting section](troubleshooting.md).
|
||||
@@ -1,59 +0,0 @@
|
||||
# NymVPN alpha GUI: Guide for Mac OS
|
||||
|
||||
```admonish info
|
||||
NymVPN is an experimental software and it's for [testing](./testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
### Installation
|
||||
|
||||
1. Create a directory `~/nym-vpn-latest`
|
||||
```sh
|
||||
mkdir -p "$HOME/nym-vpn-latest"
|
||||
```
|
||||
2. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for MacOS
|
||||
3. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
|
||||
```sh
|
||||
echo "<SHA_STRING>" | shasum -a 256 -c
|
||||
|
||||
# choose a correct one according to your binary, this is just an example
|
||||
echo "da4c0bf8e8b52658312d341fa3581954cfcb6efd516d9a448c76d042a454b5df nym-vpn-desktop_0.0.3_macos_x86_64.zip" | shasum -a 256 -c
|
||||
```
|
||||
4. Extract files with `unzip` command or manually as you are used to
|
||||
5. Move to the application directory and make executable
|
||||
```sh
|
||||
cd "macos/nym-vpn.app/Contents/MacOS"
|
||||
|
||||
chmod u+x nym-vpn
|
||||
```
|
||||
6. Move `nym-vpn` to your `~/nym-vpn-latest` directory
|
||||
```sh
|
||||
mv nym-vpn "$HOME/nym-vpn-latest"
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
7. Create the configuration file by opening a text editor and saving the lines below as `config.toml` in the same directory `~/nym-vpn-latest`
|
||||
```toml
|
||||
env_config_file = ".env"
|
||||
entry_node_location = "DE" # two letters country code
|
||||
# You can choose different entry by entering one of the following two letter country codes:
|
||||
# DE, UK, FR, IE
|
||||
```
|
||||
8. Create testnet configuration file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `.env` in the same directory `~/nym-vpn-latest`
|
||||
```sh
|
||||
curl -L "https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env" -o "$HOME/nym-vpn-latest/.env"
|
||||
```
|
||||
## Run NymVPN
|
||||
|
||||
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
|
||||
|
||||
Open terminal in your `~/nym-vpn-latest` directory and run:
|
||||
```sh
|
||||
sudo ./nym-vpn
|
||||
```
|
||||
|
||||
In case of errors check out the [troubleshooting](troubleshooting.html#installing-gui-on-macos-not-working) section.
|
||||
@@ -1,43 +0,0 @@
|
||||
# NymVPN Application (GUI)
|
||||
|
||||
```admonish info
|
||||
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
|
||||
|
||||
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
|
||||
```
|
||||
|
||||
This is the alpha version of NymVPN application - the GUI. A demo of how the client will look like for majority of day-to-day users. For qualitative testing the [CLI](cli.md) is a necessity but to run the GUI holds the same importance as it provides the user with an experience of the actual app and the developers with a valuable feedback from the users.
|
||||
|
||||
Follow the simple [automated script](#automated-script-for-gui-installation) below to install and run NymVPN GUI. If you prefer to do a manual setup follow the steps in the guide for [Linux](gui-linux.md) or [MacOS](gui-mac.md).
|
||||
|
||||
## Automated Script for GUI Installation
|
||||
|
||||
We wrote a [script](https://gist.github.com/serinko/e0a9f7ff3d79e974ec6f6783caa1137e) which does download of dependencies and the application, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
|
||||
|
||||
1. Open a terminal window in a directory where you want the script and NymVPN binary be downloaded and run
|
||||
```sh
|
||||
curl -o nym-vpn-desktop-install-run.sh -L https://gist.githubusercontent.com/serinko/e0a9f7ff3d79e974ec6f6783caa1137e/raw/064c0d42af6a187b4f62998a0c2e6a84c319eeef/nym-vpn-desktop-install-run.sh
|
||||
```
|
||||
|
||||
2. Make the script executable
|
||||
```sh
|
||||
chmod u+x nym-vpn-desktop-install-run.sh
|
||||
```
|
||||
|
||||
3. Start the script as root, turn off any VPN and run
|
||||
```sh
|
||||
sudo -E ./nym-vpn-desktop-install-run.sh
|
||||
```
|
||||
|
||||
4. Follow the prompts in the program
|
||||
|
||||
To start the application again, reconnect your wifi and run
|
||||
```sh
|
||||
# Linux
|
||||
sudo -E ~/nym-vpn-latest/nym-vpn_0.0.3_amd64.AppImage
|
||||
|
||||
# MacOS
|
||||
sudo $nym_vpn_dir/nym-vpn
|
||||
```
|
||||
|
||||
In case of errors check out the [troubleshooting](troubleshooting.md#installing-gui-on-macos-not-working) section.
|
||||
@@ -1,54 +0,0 @@
|
||||
# NymVPN alpha
|
||||
|
||||
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/897010658?h=1f55870fe6&badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="NYMVPN alpha demo 37C3"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
|
||||
|
||||
**Nym proudly presents NymVPN alpha** - a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features).
|
||||
|
||||
|
||||
**You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md) on the Sandbox testnet environment as well as provide some scripts for [qualitative testing](testing.md).
|
||||
|
||||
**Here is how**
|
||||
|
||||
1. Go to the NymVPN [testers form]({{nym_vpn_form_url}})
|
||||
2. Please consent to the GDPR so we can use the results
|
||||
3. To test the GUI, [go here](gui.md)
|
||||
4. To test the CLI, [go here](cli.md)
|
||||
5. Run [qualitative testing script](testing.md)
|
||||
6. Fill and submit the [form!]({{nym_vpn_form_url}})
|
||||
7. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
|
||||
|
||||
***NymVPN alpha testing will last from 15th of January - 15th of February.***
|
||||
|
||||
*NOTE: NymVPN alpha is experimental software for [testing purposes](testing.md) only.*
|
||||
|
||||
|
||||
## NymVPN Overview
|
||||
|
||||
To understand what's under the hood of NymVPN and the mixnet, we recommend interested developers to begin with [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html) pages.
|
||||
|
||||
The default setup of NymVPN is to run in 5-hop mode (mixnet):
|
||||
|
||||
```
|
||||
┌─►mix──┐ mix mix
|
||||
│ │
|
||||
Entry │ │ Exit
|
||||
client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet
|
||||
│ │
|
||||
│ │
|
||||
mix └─►mix──┘ mix
|
||||
```
|
||||
|
||||
Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateways, and is not passed between Mix Nodes.
|
||||
|
||||
The client can optionally do the first hop (local client to Entry Gateway) using Wireguard. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device.
|
||||
|
||||
## NymVPN Resources & Guides
|
||||
|
||||
* [NymVPN webpage](https://nymvpn.com)
|
||||
* [Alpha release page]({{nym_vpn_latest_binary_url}})
|
||||
* [NymVPN application (GUI) guide](gui.md)
|
||||
* [NymVPN Command Line Interface (CLI) guide](cli.md)
|
||||
* [Testing scripts](testing.md)
|
||||
* [Troubleshooting](troubleshooting.md)
|
||||
* [NymVPN FAQ](faq.md)
|
||||
* [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat)
|
||||