Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f3cc665d78 | |||
| f557e000a0 | |||
| f12c909f5a | |||
| 52736881db | |||
| c85ac8e54d | |||
| c4be55e824 | |||
| e62f5833e0 | |||
| ba803943d8 | |||
| 6e9eb26e27 | |||
| caf33095d6 | |||
| 0173bc748b | |||
| 9b93b30aed | |||
| 79d9ddd463 | |||
| ca512ca1ad | |||
| 0f05f6e1ee | |||
| 06e656840a | |||
| 48391d2252 | |||
| 1a6334f548 | |||
| 50da1b1606 | |||
| df010ef304 | |||
| 41caad4dbf | |||
| 4262e2e2f6 | |||
| 610f7e88ca | |||
| 05a39aa5f5 | |||
| 784ee5ace8 | |||
| 73fe7ebec7 | |||
| 7da2ce362d | |||
| f28e0b529e | |||
| 6e9a588c1a | |||
| f3442c6964 | |||
| 241169140e | |||
| 143036c2a2 | |||
| 043437a0c3 | |||
| 1370192823 | |||
| 07037341c5 | |||
| 5406396c3c | |||
| bd50119152 | |||
| d941d92571 | |||
| 06a96fa74a | |||
| 7129de4373 | |||
| 14d0d5dcbb | |||
| 37958ccb4e | |||
| 7e16920358 | |||
| 5df5918176 | |||
| 0e906b1a3d | |||
| eb7305e31c | |||
| e38b8fd419 | |||
| 19a0fb3f38 | |||
| 6fbb6539ef | |||
| 199817bed0 | |||
| 5ce2e21abc | |||
| f2383b5cb0 | |||
| 99c972e880 | |||
| b169b6b438 | |||
| 47d0c0ffa2 | |||
| 3307e7e0fc | |||
| 70d0aabbc1 | |||
| b83e756650 | |||
| b10aa52eca | |||
| 9e33454dc2 | |||
| a088d64d57 | |||
| 7c5183700e | |||
| 00ca4d2afa | |||
| cf234ecf82 | |||
| b48dc0b38a | |||
| 0294febd63 | |||
| 5fcaacc39a | |||
| 30000126d1 | |||
| 00179d563b | |||
| 4a4b0ab7e0 | |||
| b3c7801f73 | |||
| 4716d278ce | |||
| f6f2cd7e17 | |||
| 2084095773 | |||
| aa02f33add | |||
| da9d743f39 | |||
| 49cf33f6d7 | |||
| 65d93b2b18 | |||
| a17d36fd89 | |||
| d33967f60c | |||
| cfef1f8325 | |||
| 8270204c7e | |||
| 9d1adf9884 | |||
| 49dcc7e894 | |||
| 3fdd89035b | |||
| 1aad2a3a7a | |||
| fa3277e18b | |||
| 3d5a3ad958 | |||
| 722486ca68 | |||
| 96d1861955 | |||
| f9e5a1159d | |||
| 4e994f2b92 | |||
| a65da367a3 | |||
| e61281e25e | |||
| 0ff512f373 | |||
| d47f7afbf6 | |||
| 0d21f2f39d |
@@ -1,4 +1,4 @@
|
||||
name: CD docs
|
||||
name: cd-docs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release --all
|
||||
args: --workspace --release
|
||||
- name: Install mdbook
|
||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook)
|
||||
- name: Install mdbook plugins
|
||||
@@ -39,30 +39,6 @@ jobs:
|
||||
run: cd documentation && ./build_all_to_dist.sh
|
||||
continue-on-error: false
|
||||
|
||||
- name: Deploy branch master to dev
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "dist/docs/"
|
||||
REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }}
|
||||
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/
|
||||
EXCLUDE: "/node_modules/"
|
||||
|
||||
- name: Deploy branch master to prod
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "dist/docs/"
|
||||
REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }}
|
||||
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/
|
||||
EXCLUDE: "/node_modules/"
|
||||
|
||||
- name: Post process
|
||||
run: cd documentation && ./post_process.sh
|
||||
continue-on-error: false
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
name: Run config checks on all binaries
|
||||
name: ci-binary-config-checker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -35,14 +35,14 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.69.0
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '112'
|
||||
version: '114'
|
||||
|
||||
- name: Build release contracts
|
||||
run: make contracts
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release --all
|
||||
args: --workspace --release
|
||||
- name: Install mdbook
|
||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
|
||||
- name: Install mdbook plugins
|
||||
@@ -35,6 +35,7 @@ jobs:
|
||||
--vers "^1.8.0" mdbook-admonish --force && cargo install --vers \
|
||||
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
|
||||
&& cargo install --vers "^0.7.7" mdbook-linkcheck \
|
||||
&& cargo install mdbook-cmdrun \
|
||||
# && cd documentation \
|
||||
# && mdbook-admonish install dev-portal \
|
||||
# && mdbook-admonish install docs \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: CI for Nym API Tests
|
||||
name: ci-nym-api-tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
||||
- name: Install npm
|
||||
run: npm install
|
||||
|
||||
|
||||
- name: Node v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
|
||||
@@ -33,6 +33,12 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path nym-connect/desktop/Cargo.toml --all -- --check
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -45,12 +51,6 @@ jobs:
|
||||
command: test
|
||||
args: --manifest-path nym-connect/desktop/Cargo.toml --workspace
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path nym-connect/desktop/Cargo.toml --all -- --check
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
name: ci-nym-vpn-ui-js
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src/**'
|
||||
- 'nym-vpn/ui/package.json'
|
||||
- 'nym-vpn/ui/index.html'
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src/**'
|
||||
@@ -14,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
name: ci-nym-vpn-ui-rust
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src-tauri/**'
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src-tauri/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted, custom-linux]
|
||||
runs-on: custom-linux
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
CARGOTOML_PATH: ./nym-vpn/ui/src-tauri/Cargo.toml
|
||||
@@ -31,18 +29,19 @@ jobs:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Prepare build
|
||||
working-directory: nym-vpn/ui/
|
||||
run: mkdir dist
|
||||
run: mkdir nym-vpn/ui/dist
|
||||
|
||||
- name: Check build
|
||||
working-directory: nym-vpn/ui/src-tauri
|
||||
run: cargo build --release --lib --features custom-protocol
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --lib --features custom-protocol
|
||||
|
||||
# - name: Run all tests
|
||||
# uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: test
|
||||
# args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace
|
||||
# args: --manifest-path ${{ env.CARGOTOML_PATH }}
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -50,15 +49,15 @@ jobs:
|
||||
command: fmt
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all -- --check
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
- 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 }} --workspace --all-features
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features
|
||||
|
||||
- name: Run clippy
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace --all-features -- -D warnings
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features --all-targets -- -D warnings
|
||||
|
||||
@@ -4,26 +4,26 @@ on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '14 1 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust: [stable, beta]
|
||||
os: [custom-linux, windows10, custom-runner-mac-m1]
|
||||
os: [ubuntu-20.04, windows10, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
continue-on-error: true
|
||||
if: matrix.os == 'custom-linux'
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -32,6 +32,12 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install Protoc
|
||||
uses: arduino/setup-protoc@v2
|
||||
if: matrix.os == 'macos-latest'
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -42,13 +48,18 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace
|
||||
args: --release --workspace
|
||||
|
||||
- name: Build examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
args: --release --workspace --examples
|
||||
|
||||
- name: Set debug to false
|
||||
run: |
|
||||
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
|
||||
git diff
|
||||
|
||||
- name: Run unit tests
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
name: nightly-nym-connect-build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '14 1 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest, windows10]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
MANIFEST_PATH: --manifest-path nym-connect/desktop/Cargo.toml
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: ${{ env.MANIFEST_PATH }} --all -- --check
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: ${{ env.MANIFEST_PATH }} --release --workspace
|
||||
|
||||
- name: Unit tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets -- -D warnings
|
||||
|
||||
notification:
|
||||
needs: build
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Matrix - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "nym-connect-desktop-nightly-build"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -5,27 +5,24 @@ on:
|
||||
schedule:
|
||||
- cron: '14 1 * * *'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-wallet
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [custom-ubuntu-20.04, macos-latest, windows10]
|
||||
os: [ubuntu-20.04, macos-latest, windows10]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
MANIFEST_PATH: --manifest-path nym-wallet/Cargo.toml
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
if: matrix.os == 'custom-ubuntu-20.04'
|
||||
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -35,40 +32,29 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install Protoc
|
||||
uses: arduino/setup-protoc@v2
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
args: ${{ env.MANIFEST_PATH }} --all -- --check
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace
|
||||
args: ${{ env.MANIFEST_PATH }} --release --workspace
|
||||
|
||||
- name: Unit tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace
|
||||
|
||||
- name: Annotate with clippy warnings
|
||||
uses: actions-rs/clippy-check@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets -- -D warnings
|
||||
|
||||
notification:
|
||||
needs: build
|
||||
|
||||
@@ -14,13 +14,13 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.69.0
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install --version 0.112.0 wasm-opt
|
||||
run: cargo install --version 0.114.0 wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make contracts
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
@@ -28,6 +28,16 @@ jobs:
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.20"
|
||||
|
||||
- name: Install TinyGo
|
||||
uses: acifani/setup-tinygo@v1
|
||||
with:
|
||||
tinygo-version: "0.27.0"
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
|
||||
Generated
+34
-28
@@ -1158,7 +1158,7 @@ checksum = "f769ab9e8c1652d78dd0b3ec59cdaa1e2bcb3b6b39f6681b256abcdbe101cc14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"concolor-control",
|
||||
"crates-index",
|
||||
"dirs-next",
|
||||
@@ -1393,9 +1393,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.6"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -1403,13 +1403,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.6"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
||||
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex 0.5.1",
|
||||
"clap_lex 0.6.0",
|
||||
"strsim",
|
||||
"terminal_size",
|
||||
]
|
||||
@@ -1420,7 +1420,7 @@ version = "4.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7"
|
||||
dependencies = [
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1429,15 +1429,15 @@ version = "4.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29bdbe21a263b628f83fcbeac86a4416a1d588c7669dd41473bc4149e4e7d2f1"
|
||||
dependencies = [
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.2"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
@@ -1456,9 +1456,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
@@ -2871,7 +2871,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"config",
|
||||
"digest 0.10.7",
|
||||
"dirs 5.0.1",
|
||||
@@ -2941,7 +2941,7 @@ name = "explorer-api"
|
||||
version = "1.1.31"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"dotenvy",
|
||||
"humantime-serde",
|
||||
"isocountry",
|
||||
@@ -5961,7 +5961,7 @@ dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"console-subscriber",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
@@ -6066,7 +6066,7 @@ name = "nym-bin-common"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"log",
|
||||
@@ -6107,7 +6107,7 @@ dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bip39",
|
||||
"bs58 0.4.0",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"dotenvy",
|
||||
@@ -6132,7 +6132,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"bs58 0.4.0",
|
||||
"cfg-if",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"comfy-table",
|
||||
"cosmrs",
|
||||
"cosmwasm-std",
|
||||
@@ -6176,7 +6176,7 @@ dependencies = [
|
||||
name = "nym-client"
|
||||
version = "1.1.31"
|
||||
dependencies = [
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
@@ -6215,6 +6215,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.4",
|
||||
"cfg-if",
|
||||
"clap 4.4.7",
|
||||
"dashmap",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
@@ -6523,7 +6524,7 @@ dependencies = [
|
||||
"atty",
|
||||
"bip39",
|
||||
"bs58 0.4.0",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"colored",
|
||||
"dashmap",
|
||||
"dirs 4.0.0",
|
||||
@@ -6540,6 +6541,7 @@ dependencies = [
|
||||
"nym-credentials",
|
||||
"nym-crypto",
|
||||
"nym-gateway-requests",
|
||||
"nym-ip-packet-router",
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnode-common",
|
||||
"nym-network-defaults",
|
||||
@@ -6644,14 +6646,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-ip-forwarder"
|
||||
name = "nym-ip-packet-router"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-client-core",
|
||||
"nym-config",
|
||||
"nym-sdk",
|
||||
"nym-service-providers-common",
|
||||
"nym-sphinx",
|
||||
"nym-task",
|
||||
"serde",
|
||||
@@ -6697,9 +6701,10 @@ name = "nym-mixnode"
|
||||
version = "1.1.32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"bs58 0.4.0",
|
||||
"cfg-if",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"colored",
|
||||
"cpu-cycles",
|
||||
"cupid",
|
||||
@@ -6714,6 +6719,7 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnode-common",
|
||||
"nym-node",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-pemstore",
|
||||
"nym-sphinx",
|
||||
@@ -6726,10 +6732,10 @@ dependencies = [
|
||||
"opentelemetry",
|
||||
"pretty_env_logger",
|
||||
"rand 0.7.3",
|
||||
"rocket",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"toml 0.5.11",
|
||||
@@ -6818,7 +6824,7 @@ dependencies = [
|
||||
"async-file-watcher",
|
||||
"async-trait",
|
||||
"bs58 0.4.0",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
@@ -6980,7 +6986,7 @@ name = "nym-nr-query"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-network-defaults",
|
||||
@@ -7105,7 +7111,7 @@ dependencies = [
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.31"
|
||||
dependencies = [
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
@@ -9532,7 +9538,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo-edit",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"semver 1.0.20",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -10294,7 +10300,7 @@ name = "ssl-inject"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.4.6",
|
||||
"clap 4.4.7",
|
||||
"hex",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
+2
-1
@@ -90,7 +90,7 @@ members = [
|
||||
"sdk/lib/socks5-listener",
|
||||
"sdk/rust/nym-sdk",
|
||||
"service-providers/common",
|
||||
"service-providers/ip-forwarder",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
@@ -138,6 +138,7 @@ axum = "0.6.20"
|
||||
base64 = "0.21.4"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
boringtun = { git = "https://github.com/cloudflare/boringtun", rev = "e1d6360d6ab4529fc942a078e4c54df107abe2ba" }
|
||||
clap = "4.4.7"
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.3.0"
|
||||
cosmwasm-schema = "=1.3.0"
|
||||
|
||||
@@ -93,10 +93,6 @@ $(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unkn
|
||||
$(eval $(call add_cargo_workspace,wallet,nym-wallet))
|
||||
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
|
||||
|
||||
# OVERRIDE: wasm-opt fails if the binary has been built with the latest rustc.
|
||||
# Pin to the last working version.
|
||||
contracts_BUILD_RELEASE_TOOLCHAIN := +1.69.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# SDK
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -144,7 +140,7 @@ contracts: build-release-contracts wasm-opt-contracts
|
||||
|
||||
wasm-opt-contracts:
|
||||
for contract in $(CONTRACTS_WASM); do \
|
||||
wasm-opt --disable-sign-ext -Os $(CONTRACTS_OUT_DIR)/$$contract -o $(CONTRACTS_OUT_DIR)/$$contract; \
|
||||
wasm-opt --signext-lowering -Os $(CONTRACTS_OUT_DIR)/$$contract -o $(CONTRACTS_OUT_DIR)/$$contract; \
|
||||
done
|
||||
|
||||
# Consider adding 's' to make plural consistent (beware: used in github workflow)
|
||||
|
||||
+85
-3
@@ -1,8 +1,90 @@
|
||||
Critical bug or security issue 💥
|
||||
|
||||
If you're here because you're trying to figure out how to notify us of a security issue, go to Discord or Matrix, and alert the core engineers:
|
||||
If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to:
|
||||
|
||||
Jedrzej Stuczynski, discord: "Jedrzej | Nym#5666" , matrix: @jstuczyn:nymtech.chat
|
||||
Mark Sinclair | discord: marknym#8088 , matrix: @mark:nymtech.chat
|
||||
```
|
||||
security@nymte.ch
|
||||
```
|
||||
|
||||
Encrypted with our public key which is available below in plain text and also on keyservers:
|
||||
|
||||
```
|
||||
pub rsa4096 2023-10-30 [SC] [expire : 2026-10-29]
|
||||
24B2592E801A5AAA8666C8BA7C3C727F05090550
|
||||
uid [ ultime ] Security Nym Technologies <security@nymte.ch>
|
||||
sub rsa4096 2023-10-30 [E] [expire : 2026-10-29]
|
||||
|
||||
```
|
||||
|
||||
The fingerprint of the key is on the second line above.
|
||||
|
||||
If you need to chat __urgently__ to our team for a __critical__ security issue:
|
||||
|
||||
go to Matrix, and alert the core engineers with a private direct message:
|
||||
|
||||
Jedrzej Stuczynski @jstuczyn:nymtech.chat
|
||||
Mark Sinclair @mark:nymtech.chat
|
||||
Raphaël Walther @raphael:nymtech.chat
|
||||
|
||||
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
|
||||
|
||||
If you don't know what Matrix is, you can follow this documentation to create an account on this federation of instant messaging servers:
|
||||
|
||||
[Matrix for Instant Messaging](https://matrix.org/docs/chat_basics/matrix-for-im/)
|
||||
|
||||
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBGU/XpcBEAC+ykz0yxn8FferjEBooptXlOH/v/28aa0Nv8DfImTgj9BNY5cR
|
||||
UdLk+Wa3CSXQVE7PIsi0egEjAMfyxPEywbvPlgklW4XAKDVUCf3gxpQNN47VuVgV
|
||||
VwrN0VBurhhIKoEw9daO6A0P44+6nmXGIfUulCr4fMxYq82SOooog/j5w0/LfITu
|
||||
rQXxVABLkXHGN/NGf4BE52QI/ppeXWoshlNVU1wdZIIYWwte+9ukikWpN+LYfJUR
|
||||
ybtyCjQ4Gdf8ap1GmkKHmAru24wbUuFsBWGVgHsXAwYlKxyiNGR9YwgAxmFk6vNf
|
||||
1PqKGO3i4erx5X/+mzylzNbFlCqFuksZRyUSDZvQ8fxkm8ra1zWbO38eOTp8Vhgg
|
||||
SKfRTzOKeZYURZicJPxmEIfA88U4tx+YWJ54YWT/gERZkjIJL5mzIuY9UulVvKUM
|
||||
vMFUIzBMHOPXH16036zGyFMC1esRd2qqil4b9KtLgCOkrD1VgpjcveoA0VyMJCN6
|
||||
LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A
|
||||
fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj
|
||||
IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB
|
||||
tC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW10ZS5jaD6J
|
||||
AlQEEwEKAD4WIQQkslkugBpaqoZmyLp8PHJ/BQkFUAUCZT9elwIbAwUJBaOagAUL
|
||||
CQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB8PHJ/BQkFUL7dD/9zO73uI5VR+SWx
|
||||
PFmJW+9QsPiQbVRvGwNZurctmQ2s2Pe0vHRELFeqD5oYvSx2Lequ3Ir+zn/C3kDM
|
||||
kNs40obSL6jCBiLPkxEY0JqzPM9jZr7EjvlibWV3f6DxooRIqEyfN57I3OBGlqZE
|
||||
0Mx7sQuCcgau8C70DF952QhKUwXC2cmpmDKHVEEoio1xGSD4dQhGapCB32RQGtna
|
||||
OGfAO9celNMvSq0Lp+aJxeACmWFY5T4/y79JPcT5vSs/yEIRmaH/fn2piwaFBsIq
|
||||
gHJJMxO3740P1hF8j7KWUoUofuFaEALHBpEpjWTOj8ej1wmFlu+5F+jSVoc781Wb
|
||||
ZZXu04cOBXnGTogzSxMpBe9TtLb28zd6WzFotC25KTI3pngMzXsQGLJLOwvoZKiS
|
||||
LFjPRjg1rwobmB3Q3J2W5GYSveia0CDsZGP+g87GVVf/oD2Djpa68xyVYwIYeA6T
|
||||
3DNdS77qHiRuGiS4kWXyVjDqOICboR4uCvt09zlkBuLDdTWqWYARUvZjtjs4w/Ol
|
||||
rdrBI3A88ti8fRldYaNpu17ME1ilpN44yKoJtqiWc3Tisk8eYLfx6c7FQF3PrRva
|
||||
mr7FZvhFsYML5CeNFHTEzN6Y3jjKN/60DvCfodWnWFK47Txkl8UAXGY2W9B0fWqQ
|
||||
wUVr8uLuMyyMiKbeoufi7rGOj6AMErkCDQRlP16XARAA8FGmD5J3tM1BOM1niJxZ
|
||||
JTdCauzEtxEoBL0RuqGBkR8U29sRM6DwuzjU7PwscFnBaGyU+eU73GwGkH3ozFfF
|
||||
tllYhQrhP/kkN+0rEO5Xi+nR+4JCFRqrf3nJXAAPfiksURMp8er1dUOY2/e1ZSoL
|
||||
tS+nzUivV8CfE+pgj/5YtGwPC+KYHLATkKkMELCrbW4UO06VWOqQsvr6kivXuJQQ
|
||||
LdEAMpBlADmXFG45DmPKQzsBWUgvTwyGy3LX0nys8cgpex9BH8hhr01QmGyP469s
|
||||
N3cNrtFuu8U6RAsiCD/8mlBuD3EQEU5SF0lc7kCICAZk+wElmXnimEi0TOYsbz6k
|
||||
90lteicX70rA9GNeyI76H+VSOYvWpkRwaJAgUdzrAM1o9SHASq+cZ6nD85OZioQk
|
||||
DWM6+Q+sf2oen0qJnnGmUr93kJIC0PIdgrXRrtiNfeRa1Z/H0LmREyyEMoFiVivn
|
||||
z1vVk85Oq6Sf3ltUwvmDzuuJOtsp2Qp6+x6Snn/yKauI4uf4Cf/wKUch4r6Bwgg5
|
||||
Dw49ky7lwlnALio4GIVoGLpLef93wWoDmp4Klyh3ZPf2nB0U91u3bHRUo7m+D7QJ
|
||||
98cyKtqLLzjg7szGf60pIWNWRsadYQT3bSncynqknAjOV3BCvx6/ivsnpj//QjYR
|
||||
HtviUAcQ1DBB6UC6q23FIs0AEQEAAYkCPAQYAQoAJhYhBCSyWS6AGlqqhmbIunw8
|
||||
cn8FCQVQBQJlP16XAhsMBQkFo5qAAAoJEHw8cn8FCQVQzukP/iLxjOxT+UpPR//c
|
||||
prDVSLkP4pF5bmw36U07jvqpS+/KTXsxiiQleffRabOpNLcd+K1ueavyt9nnIwHH
|
||||
tHS9kM9A7DBw3LnpEbXki46QDCCI6niGijlLOEeAWqnocwMNTT05wVVgCtO3DQP2
|
||||
MoSCcqHpXDChvOyr5d5xjYLVJhlctIMSomcVzGryjknPu0Yj/TkC/4c+m86ZWQUD
|
||||
HqMHQIuiEenvb62/F4c5OJIRZPEn70wdddkgJuJU3eHdHrnuhCkjCC93GQGbGj03
|
||||
Zqos6699y6hmPeD3U5IUv8ujwZYVCCuDm8gJfrp3R6WLfeZeK9WmTVBpCzsDg3fV
|
||||
hSwmOk6pp8DAq1/Dev3yRkFggCEyGK6c9b+a0CRBncl8e5Q0QQIzNiS/uExQP3h+
|
||||
ELJs3P0MLP+6FWhNUry09n3lnWkr1hY+v1M0GAxbfdv/tsCN1Pq/VQEz+CTqXqya
|
||||
ftWldOHWw6Hh+gtwxcHjG4MBOrO5oICQ3lh2hGwQ58cDgZYSK/OGgJ9BggFl1CcM
|
||||
0uGC0/TRCI1zt/4y+7efSZQMZkHo7VC/3MFbp2hcNejpW+BxVuwKTunFvWK3TLhq
|
||||
sSlQ5yyhqchooepsFHq9bosKFjLJC01uprBv1rinoNduOy43FbyS7JPRRspANN0R
|
||||
iC2pMbWdE0ZTQaFq6tPIg058pjqi
|
||||
=nqgX
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
@@ -20,7 +20,7 @@ futures = { workspace = true } # bunch of futures stuff, however, now that I thi
|
||||
# and the single instance of abortable we have should really be refactored anyway
|
||||
url = { workspace = true }
|
||||
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
dirs = "4.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = { workspace = true } # self explanatory
|
||||
@@ -36,7 +36,7 @@ tokio-tungstenite = { workspace = true }
|
||||
## internal
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "cli"] }
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
use crate::client::config::persistence::ClientPaths;
|
||||
use crate::client::config::template::CONFIG_TEMPLATE;
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::cli_helpers::client_init::ClientConfig;
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
|
||||
@@ -72,6 +74,24 @@ impl NymConfigTemplate for Config {
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientConfig for Config {
|
||||
fn common_paths(&self) -> &CommonClientPaths {
|
||||
&self.storage_paths.common_paths
|
||||
}
|
||||
|
||||
fn core_config(&self) -> &BaseClientConfig {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn default_store_location(&self) -> PathBuf {
|
||||
self.default_location()
|
||||
}
|
||||
|
||||
fn save_to<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||
save_formatted_config_to_file(self, path)
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
Config {
|
||||
|
||||
@@ -12,55 +12,49 @@ use crate::{
|
||||
};
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
use nym_client_core::cli_helpers::client_init::{
|
||||
initialise_client, CommonClientInitArgs, InitResultsWithConfig, InitialisableClient,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use tap::TapFallible;
|
||||
|
||||
struct NativeClientInit;
|
||||
|
||||
impl InitialisableClient for NativeClientInit {
|
||||
const NAME: &'static str = "native";
|
||||
type Error = ClientError;
|
||||
type InitArgs = Init;
|
||||
type Config = Config;
|
||||
|
||||
fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> {
|
||||
try_upgrade_config(id)
|
||||
}
|
||||
|
||||
fn initialise_storage_paths(id: &str) -> Result<(), Self::Error> {
|
||||
fs::create_dir_all(default_data_directory(id))?;
|
||||
fs::create_dir_all(default_config_directory(id))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn default_config_path(id: &str) -> PathBuf {
|
||||
default_config_filepath(id)
|
||||
}
|
||||
|
||||
fn construct_config(init_args: &Self::InitArgs) -> Self::Config {
|
||||
override_config(
|
||||
Config::new(&init_args.common_args.id),
|
||||
OverrideConfig::from(init_args.clone()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[clap(long)]
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
|
||||
/// uniformly.
|
||||
#[clap(long, conflicts_with = "gateway")]
|
||||
latency_based_selection: bool,
|
||||
|
||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||
/// potentially causing loss of access.
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
#[command(flatten)]
|
||||
common_args: CommonClientInitArgs,
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
@@ -74,40 +68,28 @@ pub(crate) struct Init {
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
|
||||
impl AsRef<CommonClientInitArgs> for Init {
|
||||
fn as_ref(&self) -> &CommonClientInitArgs {
|
||||
&self.common_args
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Init> for OverrideConfig {
|
||||
fn from(init_config: Init) -> Self {
|
||||
OverrideConfig {
|
||||
nym_apis: init_config.nym_apis,
|
||||
nym_apis: init_config.common_args.nym_apis,
|
||||
disable_socket: init_config.disable_socket,
|
||||
port: init_config.port,
|
||||
host: init_config.host,
|
||||
fastmode: init_config.fastmode,
|
||||
no_cover: init_config.no_cover,
|
||||
fastmode: init_config.common_args.fastmode,
|
||||
no_cover: init_config.common_args.no_cover,
|
||||
|
||||
nyxd_urls: init_config.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
nyxd_urls: init_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,15 +103,11 @@ pub struct InitResults {
|
||||
}
|
||||
|
||||
impl InitResults {
|
||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
fn new(res: InitResultsWithConfig<Config>) -> Self {
|
||||
Self {
|
||||
client_core: nym_client_core::init::types::InitResults::new(
|
||||
&config.base,
|
||||
address,
|
||||
gateway,
|
||||
),
|
||||
client_listening_port: config.socket.listening_port,
|
||||
client_address: address.to_string(),
|
||||
client_address: res.init_results.address.to_string(),
|
||||
client_core: res.init_results,
|
||||
client_listening_port: res.config.socket.listening_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,97 +120,14 @@ impl Display for InitResults {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_paths(id: &str) -> io::Result<()> {
|
||||
fs::create_dir_all(default_data_directory(id))?;
|
||||
fs::create_dir_all(default_config_directory(id))
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
|
||||
eprintln!("Initialising client...");
|
||||
|
||||
let id = &args.id;
|
||||
let output = args.output;
|
||||
let res = initialise_client::<NativeClientInit>(args).await?;
|
||||
|
||||
let already_init = if default_config_filepath(id).exists() {
|
||||
// in case we're using old config, try to upgrade it
|
||||
// (if we're using the current version, it's a no-op)
|
||||
try_upgrade_config(id)?;
|
||||
eprintln!("Client \"{id}\" was already initialised before");
|
||||
true
|
||||
} else {
|
||||
init_paths(id)?;
|
||||
false
|
||||
};
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let user_wants_force_register = args.force_register_gateway;
|
||||
if user_wants_force_register {
|
||||
eprintln!("Instructed to force registering gateway. This will overwrite keys!");
|
||||
}
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway;
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(args.latency_based_selection),
|
||||
false,
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
let config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
||||
|
||||
// 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(config.storage_paths.common_paths.keys.clone());
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
|
||||
let available_gateways = if let Some(hardcoded_topology) = args
|
||||
.custom_mixnet
|
||||
.map(NymTopology::new_from_file)
|
||||
.transpose()?
|
||||
{
|
||||
// hardcoded_topology
|
||||
hardcoded_topology.get_gateways()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
current_gateways(&mut rng, &config.base.client.nym_api_urls).await?
|
||||
};
|
||||
|
||||
let gateway_setup = GatewaySetup::New {
|
||||
specification: selection_spec,
|
||||
available_gateways,
|
||||
overwrite_data: register_gateway,
|
||||
};
|
||||
|
||||
let init_details =
|
||||
nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store)
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||
|
||||
let config_save_location = config.default_location();
|
||||
config.save_to_default_location().tap_err(|_| {
|
||||
log::error!("Failed to save the config file");
|
||||
})?;
|
||||
eprintln!(
|
||||
"Saved configuration file to {}",
|
||||
config_save_location.display()
|
||||
);
|
||||
|
||||
let address = init_details.client_address()?;
|
||||
|
||||
eprintln!("Client configuration completed.\n");
|
||||
|
||||
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
|
||||
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
|
||||
};
|
||||
let init_results = InitResults::new(&config, &address, &gateway_details);
|
||||
println!("{}", args.output.format(&init_results));
|
||||
let init_results = InitResults::new(res);
|
||||
println!("{}", output.format(&init_results));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -10,35 +10,14 @@ use crate::{
|
||||
use clap::Args;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Run {
|
||||
/// Id of the nym-mixnet-client we want to run.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
#[clap(long)]
|
||||
gateway: Option<identity::PublicKey>,
|
||||
#[command(flatten)]
|
||||
common_args: CommonClientRunArgs,
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
@@ -51,37 +30,19 @@ pub(crate) struct Run {
|
||||
/// Ip for the socket (if applicable) to listen for requests.
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
nym_apis: run_config.nym_apis,
|
||||
nym_apis: run_config.common_args.nym_apis,
|
||||
disable_socket: run_config.disable_socket,
|
||||
port: run_config.port,
|
||||
host: run_config.host,
|
||||
fastmode: run_config.fastmode,
|
||||
no_cover: run_config.no_cover,
|
||||
nyxd_urls: run_config.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
fastmode: run_config.common_args.fastmode,
|
||||
no_cover: run_config.common_args.no_cover,
|
||||
nyxd_urls: run_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,9 +67,9 @@ fn version_check(cfg: &Config) -> bool {
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
eprintln!("Starting client {}...", args.id);
|
||||
eprintln!("Starting client {}...", args.common_args.id);
|
||||
|
||||
let mut config = try_load_current_config(&args.id)?;
|
||||
let mut config = try_load_current_config(&args.common_args.id)?;
|
||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
||||
|
||||
if !version_check(&config) {
|
||||
@@ -116,7 +77,7 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync
|
||||
return Err(Box::new(ClientError::FailedLocalVersionCheck));
|
||||
}
|
||||
|
||||
SocketClient::new(config, args.custom_mixnet)
|
||||
SocketClient::new(config, args.common_args.custom_mixnet)
|
||||
.run_socket_forever()
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
rust-version = "1.56"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
lazy_static = "1.4.0"
|
||||
log = { workspace = true }
|
||||
pretty_env_logger = "0.4"
|
||||
@@ -21,7 +21,7 @@ url = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "cli"] }
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
|
||||
@@ -11,27 +11,50 @@ use crate::{
|
||||
};
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_client_core::cli_helpers::client_init::{
|
||||
initialise_client, CommonClientInitArgs, InitResultsWithConfig, InitialisableClient,
|
||||
};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use tap::TapFallible;
|
||||
|
||||
struct Socks5ClientInit;
|
||||
|
||||
impl InitialisableClient for Socks5ClientInit {
|
||||
const NAME: &'static str = "socks5";
|
||||
type Error = Socks5ClientError;
|
||||
type InitArgs = Init;
|
||||
type Config = Config;
|
||||
|
||||
fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> {
|
||||
try_upgrade_config(id)
|
||||
}
|
||||
|
||||
fn initialise_storage_paths(id: &str) -> Result<(), Self::Error> {
|
||||
fs::create_dir_all(default_data_directory(id))?;
|
||||
fs::create_dir_all(default_config_directory(id))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn default_config_path(id: &str) -> PathBuf {
|
||||
default_config_filepath(id)
|
||||
}
|
||||
|
||||
fn construct_config(init_args: &Self::InitArgs) -> Self::Config {
|
||||
override_config(
|
||||
Config::new(&init_args.common_args.id, &init_args.provider.to_string()),
|
||||
OverrideConfig::from(init_args.clone()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
#[command(flatten)]
|
||||
common_args: CommonClientInitArgs,
|
||||
|
||||
/// Address of the socks5 provider to send messages to.
|
||||
#[clap(long)]
|
||||
@@ -46,34 +69,6 @@ pub(crate) struct Init {
|
||||
#[clap(long, alias = "use_anonymous_sender_tag")]
|
||||
use_reply_surbs: Option<bool>,
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[clap(long)]
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
|
||||
/// uniformly.
|
||||
#[clap(long, conflicts_with = "gateway")]
|
||||
latency_based_selection: bool,
|
||||
|
||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||
/// potentially causing loss of access.
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Port for the socket to listen on in all subsequent runs
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
@@ -82,41 +77,29 @@ pub(crate) struct Init {
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
|
||||
impl AsRef<CommonClientInitArgs> for Init {
|
||||
fn as_ref(&self) -> &CommonClientInitArgs {
|
||||
&self.common_args
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Init> for OverrideConfig {
|
||||
fn from(init_config: Init) -> Self {
|
||||
OverrideConfig {
|
||||
nym_apis: init_config.nym_apis,
|
||||
nym_apis: init_config.common_args.nym_apis,
|
||||
ip: init_config.host,
|
||||
port: init_config.port,
|
||||
use_anonymous_replies: init_config.use_reply_surbs,
|
||||
fastmode: init_config.fastmode,
|
||||
no_cover: init_config.no_cover,
|
||||
fastmode: init_config.common_args.fastmode,
|
||||
no_cover: init_config.common_args.no_cover,
|
||||
geo_routing: None,
|
||||
medium_toggle: false,
|
||||
nyxd_urls: init_config.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
nyxd_urls: init_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
outfox: false,
|
||||
}
|
||||
}
|
||||
@@ -131,15 +114,11 @@ pub struct InitResults {
|
||||
}
|
||||
|
||||
impl InitResults {
|
||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
fn new(res: InitResultsWithConfig<Config>) -> Self {
|
||||
Self {
|
||||
client_core: nym_client_core::init::types::InitResults::new(
|
||||
&config.core.base,
|
||||
address,
|
||||
gateway,
|
||||
),
|
||||
socks5_listening_address: config.core.socks5.bind_adddress,
|
||||
client_address: address.to_string(),
|
||||
client_address: res.init_results.address.to_string(),
|
||||
client_core: res.init_results,
|
||||
socks5_listening_address: res.config.core.socks5.bind_adddress,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,101 +135,14 @@ impl Display for InitResults {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_paths(id: &str) -> io::Result<()> {
|
||||
fs::create_dir_all(default_data_directory(id))?;
|
||||
fs::create_dir_all(default_config_directory(id))
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
|
||||
eprintln!("Initialising client...");
|
||||
|
||||
let id = &args.id;
|
||||
let provider_address = &args.provider;
|
||||
let output = args.output;
|
||||
let res = initialise_client::<Socks5ClientInit>(args).await?;
|
||||
|
||||
let already_init = if default_config_filepath(id).exists() {
|
||||
// in case we're using old config, try to upgrade it
|
||||
// (if we're using the current version, it's a no-op)
|
||||
try_upgrade_config(id)?;
|
||||
eprintln!("SOCKS5 client \"{id}\" was already initialised before");
|
||||
true
|
||||
} else {
|
||||
init_paths(id)?;
|
||||
false
|
||||
};
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let user_wants_force_register = args.force_register_gateway;
|
||||
if user_wants_force_register {
|
||||
eprintln!("Instructed to force registering gateway. This might overwrite keys!");
|
||||
}
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway;
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(args.latency_based_selection),
|
||||
false,
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
let config = override_config(
|
||||
Config::new(id, &provider_address.to_string()),
|
||||
OverrideConfig::from(args.clone()),
|
||||
);
|
||||
|
||||
// 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(config.storage_paths.common_paths.keys.clone());
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
|
||||
let available_gateways = if let Some(hardcoded_topology) = args
|
||||
.custom_mixnet
|
||||
.map(NymTopology::new_from_file)
|
||||
.transpose()?
|
||||
{
|
||||
// hardcoded_topology
|
||||
hardcoded_topology.get_gateways()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
current_gateways(&mut rng, &config.core.base.client.nym_api_urls).await?
|
||||
};
|
||||
|
||||
let gateway_setup = GatewaySetup::New {
|
||||
specification: selection_spec,
|
||||
available_gateways,
|
||||
overwrite_data: register_gateway,
|
||||
};
|
||||
|
||||
let init_details =
|
||||
nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store)
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||
|
||||
// TODO: ask the service provider we specified for its interface version and set it in the config
|
||||
|
||||
let config_save_location = config.default_location();
|
||||
config.save_to_default_location().tap_err(|_| {
|
||||
log::error!("Failed to save the config file");
|
||||
})?;
|
||||
eprintln!(
|
||||
"Saved configuration file to {}",
|
||||
config_save_location.display()
|
||||
);
|
||||
|
||||
let address = init_details.client_address()?;
|
||||
|
||||
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
|
||||
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
|
||||
};
|
||||
let init_results = InitResults::new(&config, &address, &gateway_details);
|
||||
println!("{}", args.output.format(&init_results));
|
||||
let init_results = InitResults::new(res);
|
||||
println!("{}", output.format(&init_results));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -10,19 +10,17 @@ use crate::{
|
||||
use clap::Args;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
|
||||
use nym_client_core::client::base_client::storage::OnDiskPersistent;
|
||||
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_socks5_client_core::NymClient;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Run {
|
||||
/// Id of the nym-mixnet-client we want to run.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
#[command(flatten)]
|
||||
common_args: CommonClientRunArgs,
|
||||
|
||||
/// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
|
||||
/// While this is going to hide its actual address information, it will make the actual communication
|
||||
@@ -37,19 +35,6 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
provider: Option<Recipient>,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
#[clap(long)]
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the Nym APIs
|
||||
#[clap(long, value_delimiter = ',', group = "network")]
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
@@ -58,19 +43,6 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", group = "routing", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set geo-aware mixnode selection when sending mixnet traffic, for experiments only.
|
||||
#[clap(long, hide = true, value_parser = validate_country_group, group="routing")]
|
||||
geo_routing: Option<CountryGroup>,
|
||||
@@ -80,11 +52,6 @@ pub(crate) struct Run {
|
||||
#[clap(long, hide = true)]
|
||||
medium_toggle: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
#[clap(long, hide = true, action)]
|
||||
outfox: bool,
|
||||
}
|
||||
@@ -92,16 +59,16 @@ pub(crate) struct Run {
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
nym_apis: run_config.nym_apis,
|
||||
nym_apis: run_config.common_args.nym_apis,
|
||||
ip: run_config.host,
|
||||
port: run_config.port,
|
||||
use_anonymous_replies: run_config.use_anonymous_replies,
|
||||
fastmode: run_config.fastmode,
|
||||
no_cover: run_config.no_cover,
|
||||
fastmode: run_config.common_args.fastmode,
|
||||
no_cover: run_config.common_args.no_cover,
|
||||
geo_routing: run_config.geo_routing,
|
||||
medium_toggle: run_config.medium_toggle,
|
||||
nyxd_urls: run_config.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
nyxd_urls: run_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
outfox: run_config.outfox,
|
||||
}
|
||||
}
|
||||
@@ -136,9 +103,9 @@ fn version_check(cfg: &Config) -> bool {
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
eprintln!("Starting client {}...", args.id);
|
||||
eprintln!("Starting client {}...", args.common_args.id);
|
||||
|
||||
let mut config = try_load_current_config(&args.id)?;
|
||||
let mut config = try_load_current_config(&args.common_args.id)?;
|
||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
||||
|
||||
if !version_check(&config) {
|
||||
@@ -149,7 +116,7 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error +
|
||||
let storage =
|
||||
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
|
||||
.await?;
|
||||
NymClient::new(config.core, storage, args.custom_mixnet)
|
||||
NymClient::new(config.core, storage, args.common_args.custom_mixnet)
|
||||
.run_forever()
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
use crate::config::template::CONFIG_TEMPLATE;
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::cli_helpers::client_init::ClientConfig;
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
|
||||
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
||||
@@ -69,6 +71,24 @@ impl NymConfigTemplate for Config {
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientConfig for Config {
|
||||
fn common_paths(&self) -> &CommonClientPaths {
|
||||
&self.storage_paths.common_paths
|
||||
}
|
||||
|
||||
fn core_config(&self) -> &BaseClientConfig {
|
||||
&self.core.base
|
||||
}
|
||||
|
||||
fn default_store_location(&self) -> PathBuf {
|
||||
self.default_location()
|
||||
}
|
||||
|
||||
fn save_to<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||
save_formatted_config_to_file(self, path)
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
|
||||
Config {
|
||||
|
||||
@@ -9,7 +9,7 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
clap_complete = "4.0"
|
||||
clap_complete_fig = "4.0"
|
||||
log = { workspace = true }
|
||||
|
||||
@@ -11,6 +11,7 @@ rust-version = "1.66"
|
||||
async-trait = { workspace = true }
|
||||
base64 = "0.21.2"
|
||||
cfg-if = "1.0.0"
|
||||
clap = { workspace = true, optional = true }
|
||||
dashmap = { workspace = true }
|
||||
dirs = "4.0"
|
||||
futures = { workspace = true }
|
||||
@@ -92,6 +93,7 @@ sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macro
|
||||
|
||||
[features]
|
||||
default = []
|
||||
cli = ["clap"]
|
||||
fs-surb-storage = ["sqlx"]
|
||||
wasm = ["nym-gateway-client/wasm"]
|
||||
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::disk_persistence::CommonClientPaths;
|
||||
use crate::error::ClientCoreError;
|
||||
use crate::{
|
||||
client::{
|
||||
base_client::storage::gateway_details::OnDiskGatewayDetails,
|
||||
key_manager::persistence::OnDiskKeys,
|
||||
},
|
||||
init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup, InitResults},
|
||||
};
|
||||
use log::info;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_topology::NymTopology;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub trait InitialisableClient {
|
||||
const NAME: &'static str;
|
||||
type Error: From<ClientCoreError>;
|
||||
type InitArgs: AsRef<CommonClientInitArgs>;
|
||||
type Config: ClientConfig;
|
||||
|
||||
fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error>;
|
||||
|
||||
fn initialise_storage_paths(id: &str) -> Result<(), Self::Error>;
|
||||
|
||||
fn default_config_path(id: &str) -> PathBuf;
|
||||
|
||||
fn construct_config(init_args: &Self::InitArgs) -> Self::Config;
|
||||
}
|
||||
|
||||
pub trait ClientConfig {
|
||||
fn common_paths(&self) -> &CommonClientPaths;
|
||||
|
||||
fn core_config(&self) -> &crate::config::Config;
|
||||
|
||||
fn default_store_location(&self) -> PathBuf;
|
||||
|
||||
fn save_to<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()>;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cli", derive(clap::Args))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CommonClientInitArgs {
|
||||
/// Id of client we want to create config for.
|
||||
#[cfg_attr(feature = "cli", clap(long))]
|
||||
pub id: String,
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[cfg_attr(feature = "cli", clap(long))]
|
||||
pub gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
|
||||
/// uniformly.
|
||||
#[cfg_attr(feature = "cli", clap(long, conflicts_with = "gateway"))]
|
||||
pub latency_based_selection: bool,
|
||||
|
||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||
/// potentially causing loss of access.
|
||||
#[cfg_attr(feature = "cli", clap(long))]
|
||||
pub force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)
|
||||
)]
|
||||
pub nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)
|
||||
)]
|
||||
pub nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[cfg_attr(feature = "cli", clap(long, group = "network", hide = true))]
|
||||
pub custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub no_cover: bool,
|
||||
}
|
||||
|
||||
pub struct InitResultsWithConfig<T> {
|
||||
pub config: T,
|
||||
pub init_results: InitResults,
|
||||
}
|
||||
|
||||
pub async fn initialise_client<C>(
|
||||
init_args: C::InitArgs,
|
||||
) -> Result<InitResultsWithConfig<C::Config>, C::Error>
|
||||
where
|
||||
C: InitialisableClient,
|
||||
{
|
||||
info!("initialising {} client", C::NAME);
|
||||
|
||||
let common_args = init_args.as_ref();
|
||||
let id = &common_args.id;
|
||||
|
||||
let already_init = if C::default_config_path(id).exists() {
|
||||
// in case we're using old config, try to upgrade it
|
||||
// (if we're using the current version, it's a no-op)
|
||||
C::try_upgrade_outdated_config(id)?;
|
||||
eprintln!("{} client \"{id}\" was already initialised before", C::NAME);
|
||||
true
|
||||
} else {
|
||||
C::initialise_storage_paths(id)?;
|
||||
false
|
||||
};
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let user_wants_force_register = common_args.force_register_gateway;
|
||||
if user_wants_force_register {
|
||||
eprintln!("Instructed to force registering gateway. This might overwrite keys!");
|
||||
}
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = common_args.gateway;
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(common_args.latency_based_selection),
|
||||
false,
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
let config = C::construct_config(&init_args);
|
||||
let paths = config.common_paths();
|
||||
let core = config.core_config();
|
||||
|
||||
// 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());
|
||||
let details_store = OnDiskGatewayDetails::new(&paths.gateway_details);
|
||||
|
||||
let available_gateways = if let Some(custom_mixnet) = common_args.custom_mixnet.as_ref() {
|
||||
let hardcoded_topology = NymTopology::new_from_file(custom_mixnet).map_err(|source| {
|
||||
ClientCoreError::CustomTopologyLoadFailure {
|
||||
file_path: custom_mixnet.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
hardcoded_topology.get_gateways()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await?
|
||||
};
|
||||
|
||||
let gateway_setup = GatewaySetup::New {
|
||||
specification: selection_spec,
|
||||
available_gateways,
|
||||
overwrite_data: register_gateway,
|
||||
};
|
||||
|
||||
let init_details =
|
||||
crate::init::setup_gateway(gateway_setup, &key_store, &details_store).await?;
|
||||
|
||||
// TODO: ask the service provider we specified for its interface version and set it in the config
|
||||
|
||||
let config_save_location = config.default_store_location();
|
||||
if let Err(err) = config.save_to(&config_save_location) {
|
||||
return Err(ClientCoreError::ConfigSaveFailure {
|
||||
typ: C::NAME.to_string(),
|
||||
id: id.to_string(),
|
||||
path: config_save_location,
|
||||
source: err,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"Saved configuration file to {}",
|
||||
config_save_location.display()
|
||||
);
|
||||
|
||||
let address = init_details.client_address()?;
|
||||
|
||||
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
|
||||
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
|
||||
};
|
||||
let init_results = InitResults::new(config.core_config(), address, &gateway_details);
|
||||
|
||||
Ok(InitResultsWithConfig {
|
||||
config,
|
||||
init_results,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg_attr(feature = "cli", derive(clap::Args))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CommonClientRunArgs {
|
||||
/// Id of client we want to create config for.
|
||||
#[cfg_attr(feature = "cli", clap(long))]
|
||||
pub id: String,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
#[cfg_attr(feature = "cli", clap(long))]
|
||||
pub gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)
|
||||
)]
|
||||
pub nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)
|
||||
)]
|
||||
pub nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[cfg_attr(feature = "cli", clap(long, group = "network", hide = true))]
|
||||
pub custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
// note: we removed the 'conflicts_with = medium_toggle', but that's fine since NR
|
||||
// has defined the conflict on that field itself
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
// note: we removed the 'conflicts_with = medium_toggle', but that's fine since NR
|
||||
// has defined the conflict on that field itself
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub no_cover: bool,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod client_init;
|
||||
pub mod client_run;
|
||||
@@ -8,6 +8,7 @@ use nym_topology::gateway::GatewayConversionError;
|
||||
use nym_topology::NymTopologyError;
|
||||
use nym_validator_client::ValidatorClientError;
|
||||
use std::error::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ClientCoreError {
|
||||
@@ -132,6 +133,26 @@ pub enum ClientCoreError {
|
||||
|
||||
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
|
||||
UnsupportedWssProtocol { gateway: String },
|
||||
|
||||
#[error(
|
||||
"failed to load custom topology using path '{}'. detailed message: {source}", file_path.display()
|
||||
)]
|
||||
CustomTopologyLoadFailure {
|
||||
file_path: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"failed to save config file for client-{typ} id {id} using path '{}'. detailed message: {source}", path.display()
|
||||
)]
|
||||
ConfigSaveFailure {
|
||||
typ: String,
|
||||
id: String,
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
/// Set of messages that the client can send to listeners via the task manager
|
||||
|
||||
@@ -296,16 +296,17 @@ impl<T> GatewaySetup<T> {
|
||||
/// Struct describing the results of the client initialization procedure.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InitResults {
|
||||
version: String,
|
||||
id: String,
|
||||
identity_key: String,
|
||||
encryption_key: String,
|
||||
gateway_id: String,
|
||||
gateway_listener: String,
|
||||
pub version: String,
|
||||
pub id: String,
|
||||
pub identity_key: String,
|
||||
pub encryption_key: String,
|
||||
pub gateway_id: String,
|
||||
pub gateway_listener: String,
|
||||
pub address: Recipient,
|
||||
}
|
||||
|
||||
impl InitResults {
|
||||
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
pub fn new(config: &Config, address: Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
Self {
|
||||
version: config.client.version.clone(),
|
||||
id: config.client.id.clone(),
|
||||
@@ -313,6 +314,7 @@ impl InitResults {
|
||||
encryption_key: address.encryption_key().to_base58_string(),
|
||||
gateway_id: gateway.gateway_id.clone(),
|
||||
gateway_listener: gateway.gateway_listener.clone(),
|
||||
address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::future::Future;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod cli_helpers;
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
|
||||
@@ -11,7 +11,7 @@ bip39 = { workspace = true }
|
||||
bs58 = "0.4"
|
||||
comfy-table = "6.0.0"
|
||||
cfg-if = "1.0.0"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
cw-utils = { workspace = true }
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
|
||||
@@ -9,11 +9,12 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct AtomicVerlocResult {
|
||||
inner: Arc<RwLock<VerlocResult>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[derive(Debug, Clone, Serialize, Default)]
|
||||
pub struct VerlocResult {
|
||||
total_tested: usize,
|
||||
#[serde(with = "humantime_serde")]
|
||||
@@ -35,13 +36,6 @@ impl AtomicVerlocResult {
|
||||
}
|
||||
}
|
||||
|
||||
// this could have also been achieved with a normal #[derive(Clone)] but I prefer to be explicit about it
|
||||
pub(crate) fn clone_data_pointer(&self) -> Self {
|
||||
AtomicVerlocResult {
|
||||
inner: Arc::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn reset_results(&self, new_tested: usize) {
|
||||
let mut write_permit = self.inner.write().await;
|
||||
write_permit.total_tested = new_tested;
|
||||
|
||||
@@ -226,7 +226,7 @@ impl VerlocMeasurer {
|
||||
}
|
||||
|
||||
pub fn get_verloc_results_pointer(&self) -> AtomicVerlocResult {
|
||||
self.results.clone_data_pointer()
|
||||
self.results.clone()
|
||||
}
|
||||
|
||||
fn start_listening(&self) -> JoinHandle<()> {
|
||||
|
||||
@@ -119,6 +119,7 @@ pub struct GatewayNetworkRequesterDetails {
|
||||
pub encryption_key: String,
|
||||
|
||||
pub open_proxy: bool,
|
||||
pub exit_policy: bool,
|
||||
pub enabled_statistics: bool,
|
||||
|
||||
// just a convenience wrapper around all the keys
|
||||
@@ -140,9 +141,35 @@ impl fmt::Display for GatewayNetworkRequesterDetails {
|
||||
writeln!(f, "\taddress: {}", self.address)?;
|
||||
|
||||
writeln!(f, "\tuses open proxy: {}", self.open_proxy)?;
|
||||
writeln!(f, "\tuses exit policy: {}", self.exit_policy)?;
|
||||
writeln!(f, "\tsends statistics: {}", self.enabled_statistics)?;
|
||||
|
||||
writeln!(f, "\tallow list path: {}", self.allow_list_path)?;
|
||||
writeln!(f, "\tunknown list path: {}", self.unknown_list_path)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GatewayIpPacketRouterDetails {
|
||||
pub enabled: bool,
|
||||
|
||||
pub identity_key: String,
|
||||
pub encryption_key: String,
|
||||
|
||||
// just a convenience wrapper around all the keys
|
||||
pub address: String,
|
||||
|
||||
pub config_path: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for GatewayIpPacketRouterDetails {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "IP packet router:")?;
|
||||
writeln!(f, "\tenabled: {}", self.enabled)?;
|
||||
writeln!(f, "\tconfig path: {}", self.config_path)?;
|
||||
|
||||
writeln!(f, "\tidentity key: {}", self.identity_key)?;
|
||||
writeln!(f, "\tencryption key: {}", self.encryption_key)?;
|
||||
writeln!(f, "\taddress: {}", self.address)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
opt: wasm
|
||||
wasm-opt --disable-sign-ext -Os ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm
|
||||
wasm-opt --signext-lowering -Os ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm
|
||||
|
||||
wasm:
|
||||
RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
|
||||
|
||||
@@ -3,30 +3,33 @@
|
||||
- [Introduction](introduction.md)
|
||||
|
||||
# Binaries
|
||||
- [Pre-built Binaries](./binaries/pre-built-binaries.md)
|
||||
- [Binary Initialisation and Configuration](./binaries/init-and-config.md)
|
||||
- [Building from Source](./binaries/building-nym.md)
|
||||
|
||||
- [Pre-built Binaries](binaries/pre-built-binaries.md)
|
||||
- [Binary Initialisation and Configuration](binaries/init-and-config.md)
|
||||
- [Building from Source](binaries/building-nym.md)
|
||||
<!-- - [Version Compatibility Table](binaries/version-compatiblity.md) -->
|
||||
|
||||
# Operators Guides
|
||||
|
||||
- [Mixnet Nodes Setup](./nodes/setup-guides.md)
|
||||
- [Preliminary Steps](./preliminary-steps.md)
|
||||
- [Mix Node](./nodes/mix-node-setup.md)
|
||||
- [Gateway](./nodes/gateway-setup.md)
|
||||
- [Network Requester](./nodes/network-requester-setup.md)
|
||||
- [Nyx Validator Setup](./nodes/validator-setup.md)
|
||||
- [Maintenance](./nodes/maintenance.md)
|
||||
- [Troubleshooting](./nodes/troubleshooting.md)
|
||||
- [Mixnet Nodes Setup](nodes/setup-guides.md)
|
||||
- [Preliminary Steps](preliminary-steps.md)
|
||||
- [Mix Node](nodes/mix-node-setup.md)
|
||||
- [Gateway](nodes/gateway-setup.md)
|
||||
- [Network Requester](nodes/network-requester-setup.md)
|
||||
- [Nyx Validator Setup](nodes/validator-setup.md)
|
||||
- [Maintenance](nodes/maintenance.md)
|
||||
- [Troubleshooting](nodes/troubleshooting.md)
|
||||
|
||||
# FAQ
|
||||
|
||||
- [Mix Nodes](./faq/mixnodes-faq.md)
|
||||
- [Project Smoosh](./faq/smoosh-faq.md)
|
||||
- [Mix Nodes](faq/mixnodes-faq.md)
|
||||
- [Project Smoosh](faq/smoosh-faq.md)
|
||||
|
||||
# Legal Forum
|
||||
|
||||
- [Exit Gateway](./legal/exit-gateway.md)
|
||||
- [Exit Gateway](legal/exit-gateway.md)
|
||||
- [Switzerland](legal/swiss.md)
|
||||
- [United States](legal/united-states.md)
|
||||
|
||||
---
|
||||
# Misc.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
> We aim on purpose to make minimal changes to reward scheme and software. We're just 'smooshing' together stuff we already debugged and know works.
|
||||
> -- Harry Halpin, Nym CEO
|
||||
<p></p>
|
||||
|
||||
<br>
|
||||
This page refer to the changes which are planned to take place over Q3 and Q4 2023. As this is a transition period in the beginning (Q3 2023) the [Mix Nodes FAQ page](./mixnodes-faq.md) holds more answers to the current setup as project Smoosh refers to the eventual setup. As project Smoosh gets progressively implemented the answers on this page will become to be more relevant to the current state and eventually this FAQ page will be merged with the still relevant parts of the main Mix Nodes FAQ page.
|
||||
|
||||
If any questions are not answered or it's not clear for you in which stage project Smoosh is right now, please reach out in Node Operators [Matrix room](https://matrix.to/#/#operators:nymtech.chat).
|
||||
@@ -16,51 +16,79 @@ As we shared in our blog post article [*What does it take to build the wolds mos
|
||||
|
||||
> A nick-name by CTO Dave Hrycyszyn and Chief Scientist Claudia Diaz for the work they are currently doing to “smoosh” Nym nodes so that the same operator can serve alternately as mix node, gateway or VPN node. This requires careful calibration of the Nym token economics, for example, only nodes with the highest reputation for good quality service will be in the VPN set and have the chance to earn higher rewards.
|
||||
> By simplifying the components, adding VPN features and supporting new node operators, the aim is to widen the geographical coverage of nodes and have significant redundancy, meaning plenty of operators to be able to meet demand. This requires strong token economic incentives as well as training and support for new node operators.
|
||||
|
||||
## Technical Questions
|
||||
|
||||
### What are the changes?
|
||||
|
||||
Project smoosh will have three steps:
|
||||
|
||||
1. Combine the `gateway` and `network-requester`.
|
||||
2. Combine all the nodes in the Nym Mixnet into one binary, that is `mixnode`, `gateway` (entry and exit) and `network-requester`.
|
||||
3. Make a selection button (command/argument/flag) for operators to choose whether they want their node to provide all or just some of the functions nodes have in the Nym Mixnet. Not everyone will be able/want to run an exit `gateway` for example.
|
||||
1. Combine the `gateway` and `network-requester` into one binary ✅
|
||||
2. Create [Exit Gateway](../legal/exit-gateway.md): Take the gateway binary including network requester combined in \#1 and switch from [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) ✅
|
||||
3. Combine all the nodes in the Nym Mixnet into one binary, that is `mixnode`, `gateway` (entry and exit) and `network-requester`.
|
||||
|
||||
These three steps will be staggered over time - period of several months, and will be implemented one by one with enough time to take in feedback and fix bugs in between.
|
||||
Generally, the software will be the same, just instead of multiple binaries, there will be one Nym Mixnet node binary. Delegations will remain on as they are now, per our token economics (staking, saturation etc)
|
||||
|
||||
### What does it mean for Nym nodes operators?
|
||||
|
||||
We are exploring two potential methods for implementing binary functionality in practice and will provide information in advance. The options are:
|
||||
|
||||
1. Make a selection button (command/argument/flag) for operators to choose whether they want their node to provide all or just some of the functions nodes have in the Nym Mixnet. Nodes functioning as exit gateways (in that epoch) will then have bigger rewards due to their larger risk exposure and overhead work with the setup.
|
||||
|
||||
2. All nodes will be required to have the exit gateway functionality. All nodes are rewarded the same as now, and the difference is that a node sometimes (some epochs) may be performing as exit gateway sometimes as mix node or entry gateway adjusted according the network demand by an algorithm.
|
||||
|
||||
### Where can I read more about the exit gateway setup?
|
||||
|
||||
We created an [entire page](../legal/exit-gateway.md) about the technical and legal questions around exit gateway.
|
||||
|
||||
### What is the change from allow list to deny list?
|
||||
|
||||
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 allow list to a more permissive setup. An exit policy 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.
|
||||
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.
|
||||
|
||||
### How will the Exit policy be implemented?
|
||||
|
||||
The progression of exit policy on Gateways will have three steps:
|
||||
|
||||
1. By default the [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) filtering will be disabled and the current [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) filtering is going to continue be used. This is to prevent operators getting surprised by upgrading their Gateways (or Network requesters) and suddenly be widely open to the internet. To enable the new exit policy, operators must use `--with-exit-policy` flag or modify the `config.toml` file. ✅
|
||||
2. Relatively soon the exit policy will be part of the Gateway setup by default. To disable this exit policy, operators must use `--disable-exit-policy` flag.
|
||||
3. Further down the line, it will be the only option. Then the `allowed.list` will be completely removed.
|
||||
|
||||
Keep in mind this only relates to changes happening on Gateway and Network Requester side. Whether this will be optional or mandatory depends on the chosen [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators).
|
||||
|
||||
### Can I run a mix node only?
|
||||
|
||||
Yes, to run a mix node only is an option. However it will be less rewarded as nodes providing option for `gateway` - meaning the *new smooshed gateway* (previously `gateway` and `network requester`) - due to the work and risk the operators have in comparison to running a `mixnode` only.
|
||||
It depends which [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators) will ultimately be used. In case of the first - yes. In case of the second option, all the nodes will be setup with Exit Gateway functionality turned on.
|
||||
|
||||
## Token Economics & Rewards
|
||||
|
||||
```admonish info
|
||||
For any specifics on Nym token economics and Nym Mixnet reward system, please read the [Nym token economics paper](https://nymtech.net/nym-cryptoecon-paper.pdf).
|
||||
```
|
||||
|
||||
### What are the incentives for the node operator?
|
||||
|
||||
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic ([`Nym API`](https://github.com/nymtech/nym/tree/master/nym-api) will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
|
||||
|
||||
### How does this change the token economics?
|
||||
|
||||
The token economics will stay the same as they are, same goes for the reward algorithm. In practice the distribution of rewards will benefit more the operators who run open gateways.
|
||||
The token economics will stay the same as they are, same goes for the reward algorithm.
|
||||
|
||||
### How are the rewards distributed?
|
||||
|
||||
This depends on [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators) chosen. In case of \#1, it will look like this:
|
||||
|
||||
As each operator can choose what roles their nodes provide, the nodes which work as open gateways will have higher rewards because they are the most important to keep up and stable. Besides that the operators of gateways may be exposed to more complication and possible legal risks.
|
||||
|
||||
The nodes which are initialized to run as mix nodes and gateways will be chosen to be on top of the active set before the ones working only as a mix node.
|
||||
|
||||
We are considering to turn off the rewards for non-open gateways to incentivize operators to run the open ones. Mix nodes on 'standby' will not be rewarded (as they are not being used).
|
||||
|
||||
The more roles an operator will allow their node to provide the bigger reward ratio which will have huge performance benefits for the end-users.
|
||||
I case we go with \#2, all nodes active in the epoch will be rewarded proportionally according their work.
|
||||
|
||||
In either way, Nym will share all the specifics beforehand.
|
||||
|
||||
### How will be the staking and inflation after project Smoosh?
|
||||
|
||||
We must run tests to see how many users pay. We may need to keep inflation on if not enough people pay to keep high quality gateways on in the early stage of the transition. That would mean keeping staking on for gateways. Staking will always be on for mix nodes.
|
||||
Nym will run tests to count how much payment comes from the users of the Mixnet and if that covers the reward payments. If not, we may need to keep inflation on to secure incentives for high quality gateways in the early stage of the transition.
|
||||
|
||||
### When project smooth will be launched, it would be the mixmining pool that will pay for the gateway rewards based on amount of traffic routed ?
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Nym operators - Running Exit Gateway
|
||||
# Nym Operators Legal Forum: Running Exit Gateway
|
||||
|
||||
```admonish info
|
||||
The entire content of this page is under [Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/).
|
||||
@@ -6,8 +6,9 @@ The entire content of this page is under [Creative Commons Attribution 4.0 Inter
|
||||
|
||||
This page is a part of Nym Community Legal Forum and its content is composed by shared advices in [Node Operators Legal Forum](https://matrix.to/#/!YfoUFsJjsXbWmijbPG:nymtech.chat?via=nymtech.chat&via=matrix.org) (Matrix chat) as well as though pull requests done by the node operators directly to our [repository](https://github.com/nymtech/nym/tree/develop/documentation/operators/src), reviewed by Nym DevRels.
|
||||
|
||||
This document presents an initiative to further support Nym’s mission of allowing privacy for everyone everywhere. This would be achieved with the support of Nym node operators operating gateways and opening these to any online service with the safeguards of the [Tor Null ‘deny’ list](https://tornull.org/).
|
||||
This document presents an initiative to further support Nym’s mission of allowing privacy for everyone everywhere. This would be achieved with the support of Nym node operators operating Gateways and opening these to any online service. Such setup needs a **clear policy**, one which will remain the **same for all operators** running Nym nodes. The [proposed **Exit policy**](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) is a combination of two existing safeguards: [Tor Null ‘deny’ list](https://tornull.org/) and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php).
|
||||
|
||||
All the technical changes on the side of Nym nodes - ***Project Smoosh** - are described in the [FAQ section](../faq/smoosh-faq.md).
|
||||
|
||||
```admonish warning
|
||||
Nym core team cannot provide comprehensive legal advice across all jurisdictions. Knowledge and experience with the legalities are being built up with the help of our counsel and with you, the community of Nym node operators. We encourage Nym node operators to join the operator channels ([Element](https://matrix.to/#/#operators:nymtech.chat), [Discord](https://discord.com/invite/nym), [Telegram](https://t.me/nymchan_help_chat)) to share best practices and experiences.
|
||||
@@ -22,7 +23,7 @@ Nym core team cannot provide comprehensive legal advice across all jurisdictions
|
||||
|
||||
* Currently, Nym Gateway nodes only enable access to apps and services that are on an ‘allow’ list that is maintained by the core team.
|
||||
|
||||
* To decentralise and enable privacy for a broader range of services, this initiative will have to transition from the current ‘allow’ list to a ‘deny’ list (based on the [Tor Null advisory BL](https://tornull.org/)).
|
||||
* To decentralise and enable privacy for a broader range of services, this initiative will have to transition from the current ‘allow’ list to a ‘deny’ list - [Exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt). In accordance with the [Tor Null 'deny' list](https://tornull.org/) and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php), which are two established safeguards.
|
||||
|
||||
* This will enhance the usage and appeal of Nym products for end users. As a result, increased usage will ultimately lead to higher revenues for Nym operators.
|
||||
|
||||
@@ -39,7 +40,7 @@ Nym core team cannot provide comprehensive legal advice across all jurisdictions
|
||||
|
||||
To offer a better and more private everyday experience for its users, Nym would like them to use any online services they please, without limiting its access to a few messaging apps or crypto wallets.
|
||||
|
||||
To achieve this, operators running “gateways” would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays.
|
||||
To achieve this, operators running exit gateways would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays following this [Exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt).
|
||||
|
||||
## Pros and cons of the initiative
|
||||
|
||||
@@ -54,7 +55,7 @@ Previous setup: Running nodes supporting strict SOCKS5 app-based traffic
|
||||
| Financial | | - Low revenues for operators due to limited product traction |
|
||||
|
||||
|
||||
The new setup: Running nodes supporting traffic of any online service (with safeguards in the form of an denylist)
|
||||
The new setup: Running nodes supporting traffic of any online service (with safeguards in the form of a denylist)
|
||||
|
||||
| **Dimension** | **Pros** | **Cons** |
|
||||
| :--- | :--- | :--- |
|
||||
@@ -63,21 +64,17 @@ The new setup: Running nodes supporting traffic of any online service (with safe
|
||||
| Operational | | - Higher operational overhead, such as dealing with DMCA / abuse complaints, managing the VPS provider questions, or helping the community to maintain the denylist <br>- Administrative overhead if running nodes as a company or an entity |
|
||||
| Legal | | - Ideally requires to check legal environment with local privacy association or lawyer | Financial | - Higher revenue potential for operators due to the increase in network usage | - If not running VPS with an unlimited bandwidth plan, higher costs due to higher network usage |
|
||||
|
||||
## New gateway setup
|
||||
## Exit gateways: New setup
|
||||
|
||||
In our previous technical setup, network requesters acted as a proxy, and only made requests that match an allow list. That was a default IP based list of allowed domains stored at Nym page in a centralised fashion possibly re-defined by any Network requester operator.
|
||||
|
||||
This restricts the hosts that the NymConnect app can connect to and has the effect of selectively supporting messaging services (e.g. Telegram, Matrix) or crypto wallets (e.g. Electrum or Monero). Operators of network requesters can have confidence that the infrastructure they run only connects to a limited set of public internet hosts.
|
||||
|
||||
In the new setup, the main change is to expand this short allow list to a more permissive setup. An exit policy will constrain the hosts that the users of the Nym Mixnet and Nym VPN 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 Mixnet VPN and VPN clients (both wrapped in the same app).
|
||||
The principal change in the new configuration is to make this short allow list more permissive. Nym's [Exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) will restrict the hosts to which Nym Mixnet and Nym VPN users are permitted to connect. 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 Mixnet VPN and VPN clients (both wrapped in the same app).
|
||||
|
||||
As of now we the gateways will be defaulted to Tornull’s (note: Not affiliated with Tor) deny list - reproduction permitted under Creative Commons Attribution 3.0 United States License which is IP-based, e.g., `ExitPolicy reject 5.188.10.0/23:*`. Whether we will stick with this list, do modifications (likely) or compile another one is still a subject of discussion.
|
||||
As of now we the gateways will be defaulted to a combination of [Tor Null ‘deny’ list](https://tornull.org/) (note: Not affiliated with Tor) - reproduction permitted under Creative Commons Attribution 3.0 United States License which is IP-based, e.g., `ExitPolicy reject 5.188.10.0/23:*` and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php). Whether we will stick with this list, do modifications or compile another one is still a subject of discussion. In all cases, this policy will remain the same for all the nodes, without any option to modify it by Nym node operators to secure stable and reliable service for the end users.
|
||||
|
||||
<:--
|
||||
These policies will be either reused without modification from Tor / Tornull (license permitting), or customized and updated in a Nym crowd-sourced community effort.
|
||||
-->
|
||||
|
||||
The Gateways will display an HTML page similar to that suggested by [Tor](https://gitlab.torproject.org/tpo/core/tor/-/raw/HEAD/contrib/operator-tools/tor-exit-notice.html) for exit relays on port 80 and port 443. This will allow the operator to provide information about their Gateway, possibly including the currently configured exit policy, without having to actively communicate with law enforcement or regulatory authorities. It also makes the behaviour of the Gateway transparent and even computable (a possible feature would be to offer a machine readable form of the notice in JSON or YAML).
|
||||
For exit relays on ports 80 and 443, the gateways will exhibit an HTML page resembling the one proposed by [Tor](https://gitlab.torproject.org/tpo/core/tor/-/raw/HEAD/contrib/operator-tools/tor-exit-notice.html). By doing so, the operator will be able to disclose details regarding their gateway, including the currently configured exit policy, all without the need for direct correspondence with regulatory or law enforcement agencies. It also makes the behaviour of exit gateways transparent and even computable (a possible feature would be to offer a machine readable form of the notice in JSON or YAML).
|
||||
|
||||
We also recommend operators to check the technical advice from [Tor](https://community.torproject.org/relay/setup/exit/).
|
||||
|
||||
@@ -132,30 +129,14 @@ Useful links:
|
||||
|
||||
## Legal environment - Findings from our legal team
|
||||
|
||||
```admonish warning
|
||||
Nym core team cannot provide comprehensive legal advice across all jurisdictions. Knowledge and experience with the legalities are being built up with the help of our counsel and with you, the community of Nym node operators. We encourage Nym node operators to join the operator channels ([Element](https://matrix.to/#/#operators:nymtech.chat), [Discord](https://discord.com/invite/nym), [Telegram](https://t.me/nymchan_help_chat)) to share best practices and experiences.
|
||||
```
|
||||
The Node Operators Legal Forum pages are divided into pages according the region:
|
||||
|
||||
The Swiss legal counsel and US legal counsel have so far provided the following advice:
|
||||
- [Switzerland](./swiss.md)
|
||||
- [United States](./united-states.md)
|
||||
|
||||
### Switzerland
|
||||
See the next chapter to learn how to edit information or add findings about your jurisdiction.
|
||||
|
||||
TBD soon.
|
||||
|
||||
### United States
|
||||
|
||||
A US counsel shared the following advice:
|
||||
|
||||
The legal risk faced by VPN operators subject to United States jurisdiction depends on various statutes and regulations related to privacy, anonymity, and electronic communications. The key areas to consider are: intermediary liability and exceptions, data protection, copyright infringement, export controls, criminal law, government requests for data and assistance, and third party liability.
|
||||
|
||||
As outlined in Part A, the United States treats VPNs as telecommunications networks subject to intermediary liability protection from wrongful conduct that occurs on its network. However, such protections do have exceptions including criminal law and copyright claims that are worth considering. In the United States, I am not aware of an individual ever being prosecuted or convicted for running a node for a dVPN or a Privacy Enhancing Network.
|
||||
|
||||
However, as discussed in Part B-C, VPN operators are subject to law enforcement requests for access or assistance in obtaining access to data relevant to an investigation into allegedly unlawful conduct that was facilitated by the network as an intermediary. As shown in Part C, governments may also request assistance from node operators for certain high-level and national security targets.
|
||||
|
||||
Finally, as outlined in Parts D-G, VPN operators may also be subject to non-criminal liability including (Part D) failing to respond to notices under the DMCA, (Part E) privacy and data protection law, (Part F) third party lawsuits stemming from wrongful acts committed using the network, and (G) export control violations.
|
||||
|
||||
|
||||
## How to add legal information
|
||||
## How to edit or add legal information
|
||||
|
||||
Our aim is to establish a strong community network, sharing legal findings with each other. We would like to encourage all the current and future operators to do research about the situation in the jurisdiction they operate and update this page.
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Legal environment: Switzerland
|
||||
|
||||
```admonish info
|
||||
The entire content of this page is under [Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/).
|
||||
```
|
||||
|
||||
```admonish warning
|
||||
The following part is for informational purposes only. Nym core team cannot provide comprehensive legal advice across all jurisdictions. Knowledge and experience with the legalities are being built up with the help of our counsel and with you, the community of Nym node operators. We encourage Nym node operators to join the [Node Operator](https://matrix.to/#/#operators:nymtech.chat) and [Operators Legal Forum](https://matrix.to/#/!YfoUFsJjsXbWmijbPG:nymtech.chat?via=nymtech.chat&via=matrix.org) channels on Element to share best practices and experiences.
|
||||
```
|
||||
|
||||
## Findings from our legal team
|
||||
|
||||
> **Note:** The information shared below is in the stage of conclusions upon final confirmation. The text is a not edited exert from a legal counsel. Nym core team is asking for more clarifications.
|
||||
|
||||
### Operators of Exit Nodes
|
||||
|
||||
#### Telecoms Law
|
||||
|
||||
As well as operators of normal mixnet nodes, operators of exit nodes might be considered telecommunications providers according to the broad term of the telecommunications act (TCA).
|
||||
The regulatory consequences have already been laid out in section 5.1.2.2.1 above.
|
||||
|
||||
#### Telecoms Surveillance Law
|
||||
|
||||
Unlike normal mixnet nodes, exit nodes might have information about the communication party which uses the respective exit node (in particular its IP address). They might therefore be a target for surveillance authorities, at least at first glance.
|
||||
|
||||
However, as the IP address of the communications party is disguised on the other side of the communications through the Nym encryption infrastructure, the usual situation, where an IP address or another trace of an Internet user is found in the connection with a criminal activity (e.g., in a web server protocol), and then used in cooperation with the user’s provider to identify the user, is not going to take place.
|
||||
|
||||
The same is true for the opposite side: The node operator does not see the communication party of his user.
|
||||
|
||||
Experience has shown that Swiss investigative authorities are aware of these limitations and do not conduct investigations against individuals who operate TOR nodes, for example. In one specific case that I know of, the investigation was stopped by the police as soon as it was clear that a TOR node was being operated.
|
||||
|
||||
I therefore consider the risk for an exit node operator to become involved in a SPTA proceeding as low.
|
||||
|
||||
Nevertheless, in such a situation, exit node operators providers would have to provide the authorities with the information already available to them (Art. 22 Para. 3 SPTA), and they would have to tolerate monitoring by the authorities or by the persons commissioned by the service of the data which the monitored person transmits or stores using derived communications services (Art. 27 Para. 1 SPTA; see above, 5.1.1.2). There is no duty of data retention for providers of derived communication services, though.
|
||||
|
||||
The the risk for exit node operators of being upgraded according to Art. 22 Para. 4 SPTA is low to non existent for the reasons mentioned above.
|
||||
|
||||
#### Intelligence Service Law
|
||||
|
||||
Operators of exit nodes do not provide wire-based telecommunications services either and therefore do not fall under the IntelSA.
|
||||
|
||||
### Nym as VPN provider
|
||||
|
||||
#### Telecoms Law
|
||||
|
||||
Nym as a VPN operator might be considered a telecommunication provider under the newly revised TCA, as the term now also covers operators of Over-the-Top services which are carried out over the internet.
|
||||
|
||||
However I consider possible administrative burdens arising from this qualification as negligible (see above, 5.1.2.1).
|
||||
|
||||
#### Telecoms Surveillance Law
|
||||
|
||||
VPN providers have information about the communication party which uses the respective exit node (in particular its IP address). They might therefore be a target for surveillance authorities, at least at first glance.
|
||||
|
||||
However, for the same reason I see a risk low for exit node operators to become involved in a SPTA proceeding (the IP address is not visible to the communication partner, which is exactly the reason the Nym VPN is being used at all), I also see a low risk for Nym itself to become involved in such a proceeding (see above, 5.1.3.2).
|
||||
|
||||
#### Intelligence Service Law
|
||||
|
||||
VPN operators do not provide wire-based telecommunications services and therefore do not fall under the IntelSA.
|
||||
|
||||
### EU chat control regulation in particular
|
||||
|
||||
According to a EU commission proposal for a regulation laying down rules to prevent and combat child sexual abuse (https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX: 52022PC0209) hosting providers and providers of so-called interpersonal communication services should be obliged to perform an assessment of risks of online child sexual abuse. Additionally an obligation for certain providers should be established to detect such abuse, to report it via the EU Centre, to remove or disable access to, or to block online child sexual abuse material when so ordered.
|
||||
|
||||
'Interpersonal communications service’ means a service normally provided for remuneration that enables direct interpersonal and interactive exchange of information via electronic communications networks between a finite number of persons, whereby the persons initiating or participating in the communication determine its recipient(s) and does not include services which enable interpersonal and interactive communication merely as a minor ancillary feature that is intrinsically linked to another service (Art. 2 Point 5 Directive (EU) 2018/1972, which is also relevant for the mentioned proposal).
|
||||
|
||||
Interpersonal communications services are services that enable interpersonal and interactive exchange of information. Interactive communication entails that the service allows the recipient of the information to respond. The proposal therefore only covers services like traditional voice calls between two individuals but also all types of emails, messaging services, or group chats. Examples for services which do not meet those requirements are linear broadcasting, video on demand, websites, social networks, blogs, or exchange of information between machines (Directive (EU) 2018/1972, Consideration 17).
|
||||
|
||||
Neither the Nym encryption infrastructure nor the NYM VPN are used as means for an interactive exchange of information in the aforementioned sense (of e-mail, messaging, chats or similar).
|
||||
|
||||
I therefore consider the risk arising from the mentioned proposal for Nym as low, be it as software developer or VPN operator.
|
||||
|
||||
However, an application provider which uses the Nym encryption infrastructure to provide encrypted chat services or similar could still fall under the proposal. This might pose a commercial risk for Nym as the provider of the basic infrastructure for such services, because such services might lose their commercial value for end customers.
|
||||
|
||||
Currently the EU decision on chat control has been postponed because there is a blocking minority which can prevent the adoption of the respective parts of the law. In addition, even EU internal lawyers held that the proposal was clearly in violation of the EU charter of fundamental rights and would therefore be nullified by the EU courts in case it would still be enacted by the parliament.
|
||||
|
||||
I therefore consider the risk that the mentioned proposal is enacted by the EU authorities and finally upheld by the courts in its planned form as low.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Legal environment: United States
|
||||
|
||||
```admonish info
|
||||
The entire content of this page is under [Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/).
|
||||
```
|
||||
|
||||
```admonish warning
|
||||
The following part is for informational purposes only. Nym core team cannot provide comprehensive legal advice across all jurisdictions. Knowledge and experience with the legalities are being built up with the help of our counsel and with you, the community of Nym node operators. We encourage Nym node operators to join the [Node Operator](https://matrix.to/#/#operators:nymtech.chat) and [Operators Legal Forum](https://matrix.to/#/!YfoUFsJjsXbWmijbPG:nymtech.chat?via=nymtech.chat&via=matrix.org) channels on Element to share best practices and experiences.
|
||||
```
|
||||
|
||||
## Findings from our legal team
|
||||
|
||||
> **Note:** The information shared below is in the stage of conclusions upon final confirmation. The text is a not edited exert from a legal counsel. Nym core team is asking for more clarifications.
|
||||
|
||||
The US legal counsel have so far provided the following advice:
|
||||
|
||||
The legal risk faced by VPN operators subject to United States jurisdiction depends on various statutes and regulations related to privacy, anonymity, and electronic communications. The key areas to consider are: intermediary liability and exceptions, data protection, copyright infringement, export controls, criminal law, government requests for data and assistance, and third party liability.
|
||||
|
||||
As outlined in Part A, the United States treats VPNs as telecommunications networks subject to intermediary liability protection from wrongful conduct that occurs on its network. However, such protections do have exceptions including criminal law and copyright claims that are worth considering. In the United States, I am not aware of an individual ever being prosecuted or convicted for running a node for a dVPN or a Privacy Enhancing Network.
|
||||
|
||||
However, as discussed in Part B-C, VPN operators are subject to law enforcement requests for access or assistance in obtaining access to data relevant to an investigation into allegedly unlawful conduct that was facilitated by the network as an intermediary. As shown in Part C, governments may also request assistance from node operators for certain high-level and national security targets.
|
||||
|
||||
Finally, as outlined in Parts D-G, VPN operators may also be subject to non-criminal liability including (Part D) failing to respond to notices under the DMCA, (Part E) privacy and data protection law, (Part F) third party lawsuits stemming from wrongful acts committed using the network, and (G) export control violations.
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
|
||||
```admonish info
|
||||
As a result of [Project Smoosh](../faq/smoosh-faq.md), the current version of `nym-gateway` binary also contains `nym-network-requester` functionality which can be enabled [by the operator](./gateway-setup.md#initialising-gateway-with-network-requester). This combination is a basis of Nym exit gateway node - an essential piece in our new setup. Please read more in our [Project Smoosh FAQ](../faq/smoosh-faq.md) and [Exit Gateways Page](../legal/exit-gateway.md). We recommend operators begin to shift their setups to this new combined node, instead of operating two separate binaries.
|
||||
As a result of [Project Smoosh](../faq/smoosh-faq.md), the current version of `nym-gateway` binary also contains `nym-network-requester` functionality which can be enabled [by the operator](./gateway-setup.md#initialising-gateway-with-network-requester). This combination is a basis of ***Nym Exit Gateway*** node - an essential piece in our new setup. Please read more in our [Project Smoosh FAQ](../faq/smoosh-faq.md) and [Exit Gateway](../legal/exit-gateway.md) pages. We recommend operators begin to shift their setups to this new combined node, instead of operating two separate binaries.
|
||||
```
|
||||
|
||||
> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
## Current version
|
||||
@@ -47,58 +48,41 @@ You can also check the various arguments required for individual commands with:
|
||||
```
|
||||
> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment.
|
||||
|
||||
### Initialising your gateway
|
||||
To check available configuration options use:
|
||||
## Initialising your Gateway
|
||||
|
||||
As Nym developers build towards [Exit Gateway](../legal/exit-gateway.md) functionality, operators can now run their `nym-gateway` binary with in-build Network requester and include the our new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt). Considering the plan to [*smoosh*](../faq/smoosh-faq.md) all the nodes into one binary and have wide opened Exit Gateways, we recommend this setup, instead of operating two separate binaries.
|
||||
|
||||
```admonish warning
|
||||
Before you start an Exit Gateway, read our [Operators Legal Forum](../legal/exit-gateway.md) page and [*Project Smoosh FAQ*](../faq/smoosh-faq.md).
|
||||
```
|
||||
|
||||
```admonish info
|
||||
There has been an ongoing development with dynamic upgrades. Follow the status of the Project Smoosh [changes](../faq/smoosh-faq.md#what-are-the-changes) and the progression state of Exit policy [implementation](../faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented) to be up to date with the current design.
|
||||
```
|
||||
|
||||
### Initialising Exit Gateway
|
||||
|
||||
An operator can initialise the Exit Gateway functionality by adding Network requester with the new exit policy option:
|
||||
|
||||
```
|
||||
./nym-gateway init --help
|
||||
./nym-gateway init --id <ID> --host $(curl -4 https://ifconfig.me) --with-network-requester --with-exit-policy true
|
||||
```
|
||||
|
||||
If we follow the previous example with `<ID>` chosen `superexitgateway`, adding the `--with-network-requester` and `--with-exit-policy` flags, the outcome will be:
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway init --help -->
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway init --id superexitgateway --host $(curl -4 https://ifconfig.me) --with-network-requester --with-exit-policy true -->
|
||||
```
|
||||
~~~
|
||||
|
||||
The following command returns a gateway on your current IP with the `<ID>` of `supergateway`:
|
||||
You can see that the printed information besides *identity* and *sphinx keys* also includes a long string called *address*. This is the address to be provided to your local [socks5 client](https://nymtech.net/docs/clients/socks5-client.html) as a `--provider` if you wish to connect to your own Exit Gateway.
|
||||
|
||||
```
|
||||
./nym-gateway init --id supergateway --host $(curl ifconfig.me)
|
||||
```
|
||||
Additionally
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway init --id supergateway --host $(curl ifconfig.me) -->
|
||||
```
|
||||
~~~
|
||||
#### Add Network requester to an existing Gateway
|
||||
|
||||
The `$(curl ifconfig.me)` command above returns your IP automatically using an external service. Alternatively, you can enter your IP manually if you wish. If you do this, remember to enter your IP **without** any port information.
|
||||
|
||||
|
||||
#### Initialising gateway with network requester
|
||||
|
||||
As some of the [Project Smoosh](../faq/smoosh-faq.md) changes getting implemented, network requester is smooshed with gateways. Such combination creates an exit gateway node, needed for new more open setup.
|
||||
|
||||
An operator can initialise the exit gateway functionality by:
|
||||
|
||||
```
|
||||
./nym-gateway init --id <ID> --host $(curl ifconfig.me) --with-network-requester
|
||||
```
|
||||
|
||||
If we follow the previous example with `<ID>` chosen `superexitgateway`, adding the `--with-network-requester` flag, the outcome will be:
|
||||
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway init --id superexitgateway --host $(curl ifconfig.me) --with-network-requester -->
|
||||
```
|
||||
~~~
|
||||
|
||||
You can see that the printed information besides *identity* and *sphinx keys* also includes a long string called *address*. This is the address to be provided to your local [socks5 client](https://nymtech.net/docs/clients/socks5-client.html) as a `--provider` if you wish to connect to your own exit gateway.
|
||||
|
||||
#### Add network requester to existing gateway
|
||||
|
||||
If you already run a gateway and got it [upgraded](./maintenance.md#upgrading-your-node) to the [newest version](./gateway-setup.md#current-version), you can easily change its functionality to exit gateway. PAuse the gateway and run a command `setup-network-requester`.
|
||||
If you already [upgraded](./maintenance.md#upgrading-your-node) your Gateway to the [latest version](./gateway-setup.md#current-version) and initialised without a Network requester, you can easily change its functionality to Exit Gateway with a command `setup-network-requester`.
|
||||
|
||||
See the options:
|
||||
|
||||
@@ -112,19 +96,27 @@ See the options:
|
||||
```
|
||||
~~~
|
||||
|
||||
Run with `--enabled true` flag choosing `<ID>` as `supergateway`:
|
||||
To setup Exit Gateway functionality with our new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) add a flag `--with-exit-policy true`.
|
||||
|
||||
```
|
||||
./nym-gateway setup-network-requester --enabled true --id supergateway
|
||||
./nym-gateway setup-network-requester --enabled true --with-exit-policy true --id <ID>
|
||||
```
|
||||
|
||||
Say we have a gateway with `<ID>` as `new-gateway`, originally initialised and ran without the Exit Gateway functionality. To change the setup, run:
|
||||
|
||||
|
||||
```
|
||||
./nym-gateway setup-network-requester --enabled true --with-exit-policy true --id new-gateway
|
||||
```
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway setup-network-requester --enabled true --id supergateway -->
|
||||
<!-- cmdrun rm -rf $HOME/.nym/gateways/new-gateway -->
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway init --id new-gateway --host $(curl -4 https://ifconfig.me) && ../../../../target/release/nym-gateway setup-network-requester --enabled true --with-exit-policy true --id new-gateway -->
|
||||
```
|
||||
~~~
|
||||
|
||||
In case there are any problems, you can also change it manually by editing the gateway config stored in `/home/user/.nym/gateways/<ID>/config/config.toml` where the line under `[network_requester]` needs to be edited from `false` to `true`.
|
||||
In case there are any unexpected problems, you can also change it manually by editing the Gateway config file stored in `/home/user/.nym/gateways/<ID>/config/config.toml` where the line under `[network_requester]` needs to be edited from `false` to `true`.
|
||||
|
||||
```
|
||||
[network_requester]
|
||||
@@ -132,19 +124,52 @@ In case there are any problems, you can also change it manually by editing the g
|
||||
enabled = true
|
||||
```
|
||||
|
||||
Save, exit and restart your gateway. Now it is a post-smooshed exit gateway.
|
||||
Save, exit and restart your gateway. Now you are an operator of post-smooshed Exit gateway.
|
||||
|
||||
All information about network requester part of your exit gateway is in `/home/user/.nym/gateways/snus/config/network_requester_config.toml`.
|
||||
All information about network requester part of your exit gateway is in `/home/user/.nym/gateways/<ID>/config/network_requester_config.toml`.
|
||||
|
||||
For now you can run Gateway without Network requester or with and without the new exit policy. This will soon change as we inform in our [Project Smoosh FAQ](../faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented).
|
||||
|
||||
To read more about the configuration like whitelisted outbound requesters in `allowed.list` and other useful information, see the page [*Network Requester Whitelist*](network-requester-setup.md#using-your-network-requester).
|
||||
|
||||
|
||||
```admonish info
|
||||
Before you bond and run your gateway, please make sure the [firewall configuration](./maintenance.md#configure-your-firewall) is setup so your gateway can be reached from the outside.
|
||||
#### Initialising Gateway without Network requester
|
||||
|
||||
In case you don't want to run your Gateway with the Exit Gateway functionality, you still can run a simple Gateway.
|
||||
|
||||
To check available configuration options use:
|
||||
|
||||
```
|
||||
./nym-gateway init --help
|
||||
```
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway init --help -->
|
||||
```
|
||||
~~~
|
||||
|
||||
The following command returns a gateway on your current IP with the `<ID>` of `simple-gateway`:
|
||||
|
||||
```
|
||||
./nym-gateway init --id simple-gateway --host $(curl -4 https://ifconfig.me)
|
||||
```
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
<!-- cmdrun ../../../../target/release/nym-gateway init --id simple-gateway --host $(curl -4 https://ifconfig.me) -->
|
||||
```
|
||||
~~~
|
||||
|
||||
The `$(curl -4 https://ifconfig.me)` command above returns your IP automatically using an external service. Alternatively, you can enter your IP manually if you wish. If you do this, remember to enter your IP **without** any port information.
|
||||
|
||||
|
||||
### Bonding your gateway
|
||||
|
||||
```admonish info
|
||||
Before you bond and re-run your Gateway, please make sure the [firewall configuration](./maintenance.md#configure-your-firewall) is setup so your gateway can be reached from the outside. You can also setup WSS on your Gateway, the steps are on the [Maintenance page](./maintenance.md#configure-your-firewall) below.
|
||||
```
|
||||
|
||||
#### Via the Desktop wallet
|
||||
|
||||
You can bond your gateway via the Desktop wallet.
|
||||
@@ -156,7 +181,7 @@ You can bond your gateway via the Desktop wallet.
|
||||
3. You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature as the value of `--contract-msg` and run it.
|
||||
|
||||
```
|
||||
./nym-gatewway sign --id <YOUR_ID> --contract-msg <PAYLOAD_GENERATED_BY_THE_WALLET>
|
||||
./nym-gateway sign --id <YOUR_ID> --contract-msg <PAYLOAD_GENERATED_BY_THE_WALLET>
|
||||
```
|
||||
|
||||
It will look something like this:
|
||||
@@ -172,7 +197,7 @@ It will look something like this:
|
||||
|_| |_|\__, |_| |_| |_|
|
||||
|___/
|
||||
|
||||
(nym-gateway - version v1.1.29)
|
||||
(nym-gateway - version v1.1.31)
|
||||
|
||||
|
||||
>>> attempting to sign 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a
|
||||
@@ -194,7 +219,7 @@ It will look something like this:
|
||||
|
||||
* Your gateway is now bonded.
|
||||
|
||||
> You are asked to `sign` a transaction on bonding so that the mixnet smart contract is able to map your nym address to your node. This allows us to create a nonce for each account and defend against replay attacks.
|
||||
> You are asked to `sign` a transaction on bonding so that the Mixnet smart contract is able to map your Nym address to your node. This allows us to create a nonce for each account and defend against replay attacks.
|
||||
|
||||
#### Via the CLI (power users)
|
||||
If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.html#bond-a-mix-node) docs.
|
||||
|
||||
@@ -103,36 +103,120 @@ Running the command `df -H` will return the size of the various partitions of yo
|
||||
|
||||
If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process.
|
||||
|
||||
## Moving a node
|
||||
|
||||
In case of a need to move a node from one machine to another and avoiding to lose the delegation, here are few steps how to do it.
|
||||
## Run Web Secure Socket (WSS) on Gateway
|
||||
|
||||
The following examples transfers a mix node (in case of other nodes, change the `mixnodes` in the command for the `<NODE>` of your desire.
|
||||
Now you can run WSS on your Gateway.
|
||||
|
||||
* Pause your node process.
|
||||
### WSS on an existing Gateway
|
||||
|
||||
Assuming both machines are remote VPS.
|
||||
In case you already run a working Gateway and want to add WSS on it, here are the pre-requisites to running WSS on Gateways:
|
||||
|
||||
* Make sure your `~/.ssh/<YOUR_KEY>.pub` is in both of the machines `~/.ssh/authorized_keys` file
|
||||
* Create a `mixnodes` folder in the target VPS. Ssh in from your terminal and run:
|
||||
* You need to use the latest `nym-gateway` binary [version](./gateway-setup.md#current-version) and restart it.
|
||||
* That will add the relevant fields to update your config.
|
||||
* These two values will be added and need to be amended in your config.toml:
|
||||
|
||||
```sh
|
||||
# in case none of the nym configs was created previously
|
||||
mkdir ~/.nym
|
||||
|
||||
#in case no nym mix node was initialized previously
|
||||
mkdir ~/.nym/mixnodes
|
||||
clients_wss_port = 0
|
||||
hostname = ""
|
||||
```
|
||||
* Move the node data (keys) and config file to the new machine by opening a local terminal (as that one's ssh key is authorized in both of the machines) and running:
|
||||
|
||||
Then you can run this:
|
||||
|
||||
```sh
|
||||
scp -r -3 <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:~/.nym/mixnodes/<YOUR_ID> <TARGET_USER_NAME>@<TARGET_HOST_ADDRESS>:~/.nym/mixnodes/
|
||||
```
|
||||
* Re-run init (remember that init doesn't overwrite existing keys) to generate a config with the new listening address etc.
|
||||
* Change the node smart contract info via the wallet interface. Otherwise the keys will point to the old IP address in the smart contract, and the node will not be able to be connected, and it will fail up-time checks.
|
||||
* Re-run the node from the new location.
|
||||
port=$1 // in the example below we will use 9001
|
||||
host=$2 = // this would be a domain name registered for your Gateway for example: mainnet-gateway2.nymtech.net
|
||||
|
||||
|
||||
sed -i "s/clients_wss_port = 0/clients_wss_port = ${port}/" ${HOME}/.nym/gateways/*/config/config.toml
|
||||
sed -i "s|hostname = ''|hostname = '${host}'|" ${HOME}/.nym/gateways/*/config/config.toml
|
||||
```
|
||||
The following shell script can be run:
|
||||
|
||||
```sh
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: sudo ./install_run_caddy.sh <host_name> <port_to_run_wss>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
host=$1
|
||||
port_value=$2
|
||||
|
||||
apt install -y debian-keyring debian-archive-keyring apt-transport-https
|
||||
apt --fix-broken install
|
||||
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
|
||||
apt update
|
||||
apt install caddy
|
||||
|
||||
systemctl enable caddy.service
|
||||
|
||||
cd /etc/caddy
|
||||
|
||||
# check if Caddyfile exists, if it does, remove and insert a new one
|
||||
if [ -f Caddyfile ]; then
|
||||
echo "removing caddyfile inserting a new one"
|
||||
rm -f Caddyfile
|
||||
fi
|
||||
|
||||
cat <<EOF >> Caddyfile
|
||||
${host}:${port_value} {
|
||||
@websockets {
|
||||
header Connection *Upgrade*
|
||||
header Upgrade websocket
|
||||
}
|
||||
reverse_proxy @websockets localhost:9000
|
||||
}
|
||||
EOF
|
||||
|
||||
cat Caddyfile
|
||||
|
||||
echo "script completed successfully!"
|
||||
|
||||
systemctl restart caddy.service
|
||||
echo "have a nice day!"
|
||||
exit 0
|
||||
|
||||
```
|
||||
|
||||
Although your gateway is Now ready to use its `wss_port`, your server may not be ready - the following commands will allow you to set up a properly configured firewall using `ufw`:
|
||||
|
||||
```sh
|
||||
ufw allow 9001/tcp
|
||||
```
|
||||
|
||||
Lastly don't forget to restart your Gateway, now the API will render the WSS details for this gateway:
|
||||
|
||||
|
||||
### WSS on a new Gateway
|
||||
|
||||
These steps are for an operator who is setting up a Gateway for the first time and wants to run it with WSS.
|
||||
|
||||
New flags will need to be added to the `init` and `run` command. The `--host` option is still accepted for now, but can and should be replaced with `--listening-address`, this is the IP address which is used for receiving sphinx packets and listening to client data.
|
||||
|
||||
Another flag `--public-ips` is required; it's a comma separated list of IP’s that are announced to the `nym-api`, it is usually the address which is used for bonding.
|
||||
|
||||
If the operator wishes to run WSS, an optional `--hostname` flag is also required, that can be something like `mainnet-gateway2.nymtech.net`. Make sure to enable all necessary [ports](maintenance.md#configure-your-firewall) on the Gateway.
|
||||
|
||||
The gateway will then be accessible on something like: *http://85.159.211.99:8080/api/v1/swagger/index.html*
|
||||
|
||||
Are you seeing something like: *this node attempted to announce an invalid public address: 0.0.0.0.*?
|
||||
|
||||
Please modify `[host.public_ips]` section of your config file stored as `~/.nym/gateways/<ID>/config/config.toml`.
|
||||
|
||||
If so the flags are going to be slightly different:
|
||||
|
||||
```
|
||||
--listening-address "0.0.0.0" --public-ips "$(curl -4 https://ifconfig.me)"
|
||||
```
|
||||
|
||||
## Configure your firewall
|
||||
|
||||
## VPS Setup and Automation
|
||||
### Configure your firewall
|
||||
Although your `<NODE>` is now ready to receive traffic, your server may not be. The following commands will allow you to set up a firewall using `ufw`.
|
||||
|
||||
```sh
|
||||
@@ -153,7 +237,10 @@ Finally open your `<NODE>` p2p port, as well as ports for ssh and ports for verl
|
||||
|
||||
```sh
|
||||
# for mix node, gateway and network requester
|
||||
sudo ufw allow 1789,1790,8000,9000,22/tcp
|
||||
sudo ufw allow 1789,1790,8000,9000,9001,22/tcp
|
||||
|
||||
# In case of reverse proxy for the Gateway swagger page add:
|
||||
sudo ufw allow 8080,80/443
|
||||
|
||||
# for validator
|
||||
sudo ufw allow 1317,26656,26660,22,80,443/tcp
|
||||
@@ -166,6 +253,8 @@ sudo ufw status
|
||||
|
||||
For more information about your node's port configuration, check the [port reference table](./maintenance.md#gateway-port-reference) below.
|
||||
|
||||
## VPS Setup and Automation
|
||||
|
||||
### Automating your node with nohup, tmux and systemd
|
||||
|
||||
Although it’s not totally necessary, it's useful to have the mix node automatically start at system boot time.
|
||||
@@ -398,7 +487,7 @@ Failed to accept incoming connection - Os { code: 24, kind: Other, message: "Too
|
||||
|
||||
This means that the operating system is preventing network connections from being made.
|
||||
|
||||
#### Set the ulimit via `systemd` service file
|
||||
#### Set the `ulimit` via `systemd` service file
|
||||
|
||||
> Replace `<NODE>` variable with `nym-mixnode`, `nym-gateway` or `nym-network-requester` according the node you running on your machine.
|
||||
|
||||
@@ -481,6 +570,35 @@ username soft nofile 4096
|
||||
|
||||
Then reboot your server and restart your mix node.
|
||||
|
||||
## Moving a node
|
||||
|
||||
In case of a need to move a node from one machine to another and avoiding to lose the delegation, here are few steps how to do it.
|
||||
|
||||
The following examples transfers a mix node (in case of other nodes, change the `mixnodes` in the command for the `<NODE>` of your desire.
|
||||
|
||||
* Pause your node process.
|
||||
|
||||
Assuming both machines are remote VPS.
|
||||
|
||||
* Make sure your `~/.ssh/<YOUR_KEY>.pub` is in both of the machines `~/.ssh/authorized_keys` file
|
||||
* Create a `mixnodes` folder in the target VPS. Ssh in from your terminal and run:
|
||||
|
||||
```sh
|
||||
# in case none of the nym configs was created previously
|
||||
mkdir ~/.nym
|
||||
|
||||
#in case no nym mix node was initialized previously
|
||||
mkdir ~/.nym/mixnodes
|
||||
```
|
||||
* Move the node data (keys) and config file to the new machine by opening a local terminal (as that one's ssh key is authorized in both of the machines) and running:
|
||||
```sh
|
||||
scp -r -3 <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:~/.nym/mixnodes/<YOUR_ID> <TARGET_USER_NAME>@<TARGET_HOST_ADDRESS>:~/.nym/mixnodes/
|
||||
```
|
||||
* Re-run init (remember that init doesn't overwrite existing keys) to generate a config with the new listening address etc.
|
||||
* Change the node smart contract info via the wallet interface. Otherwise the keys will point to the old IP address in the smart contract, and the node will not be able to be connected, and it will fail up-time checks.
|
||||
* Re-run the node from the new location.
|
||||
|
||||
|
||||
## Virtual IPs and hosting via Google & AWS
|
||||
For true internet decentralization we encourage operators to use diverse VPS providers instead of the largest companies offering such services. If for some reasons you have already running AWS or Google and want to setup a `<NODE>` there, please read the following.
|
||||
|
||||
@@ -680,6 +798,7 @@ All `<NODE>`-specific port configuration can be found in `$HOME/.nym/<NODE>/<YOU
|
||||
|--------------|---------------------------|
|
||||
| `1789` | Listen for Mixnet traffic |
|
||||
| `9000` | Listen for Client traffic |
|
||||
| `9001` | WSS |
|
||||
|
||||
### Network requester port reference
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
```admonish info
|
||||
As a result of [Project Smoosh](../faq/smoosh-faq.md), the current version of `nym-gateway` binary also contains `nym-network-requester` functionality which can be enabled [by the operator](./gateway-setup.md#initialising-gateway-with-network-requester). This combination is a basis of Nym exit gateway node - an essential piece in our new setup. Please read more in our [Project Smoosh FAQ](../faq/smoosh-faq.md) and [Exit Gateways Page](../legal/exit-gateway.md). We recommend operators begin to shift their setups to this new combined node, instead of operating two separate binaries.
|
||||
```
|
||||
|
||||
> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
## Current version
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ bs58 = "0.4.0"
|
||||
bytes = "1.3.0"
|
||||
cfg-if = "1.0.0"
|
||||
chrono = { version = "0.4.24", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
config = { version = "0.13", default-features = false, features = ["toml"] }
|
||||
digest = "0.10.6"
|
||||
dirs = "5.0.0"
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
dotenvy = "0.15.6"
|
||||
humantime-serde = "1.0"
|
||||
isocountry = "0.3.2"
|
||||
|
||||
+3
-2
@@ -20,7 +20,7 @@ async-trait = { workspace = true }
|
||||
atty = "0.2"
|
||||
bip39 = { workspace = true }
|
||||
bs58 = "0.4.0"
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
colored = "2.0"
|
||||
dashmap = { workspace = true }
|
||||
dirs = "4.0"
|
||||
@@ -52,7 +52,7 @@ tokio = { workspace = true, features = [
|
||||
tokio-stream = { version = "0.1.11", features = ["fs"] }
|
||||
tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||
url = { version = "2.2", features = ["serde"] }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
# internal
|
||||
@@ -76,6 +76,7 @@ nym-task = { path = "../common/task" }
|
||||
nym-types = { path = "../common/types" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-wireguard = { path = "../common/wireguard", optional = true }
|
||||
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
|
||||
[dev-dependencies]
|
||||
tower = "0.4.13"
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
use crate::commands::upgrade_helpers;
|
||||
use crate::config::default_config_filepath;
|
||||
use crate::config::persistence::paths::default_network_requester_data_dir;
|
||||
use crate::config::persistence::paths::{
|
||||
default_ip_packet_router_data_dir, default_network_requester_data_dir,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
use log::{error, info};
|
||||
@@ -17,7 +19,7 @@ use nym_network_requester::config::BaseClientConfig;
|
||||
use nym_network_requester::{
|
||||
setup_gateway, GatewaySelectionSpecification, GatewaySetup, OnDiskGatewayDetails, OnDiskKeys,
|
||||
};
|
||||
use nym_types::gateway::GatewayNetworkRequesterDetails;
|
||||
use nym_types::gateway::{GatewayIpPacketRouterDetails, GatewayNetworkRequesterDetails};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
@@ -39,6 +41,7 @@ pub(crate) struct OverrideConfig {
|
||||
pub(crate) nyxd_urls: Option<Vec<url::Url>>,
|
||||
pub(crate) only_coconut_credentials: Option<bool>,
|
||||
pub(crate) with_network_requester: Option<bool>,
|
||||
pub(crate) with_ip_packet_router: Option<bool>,
|
||||
}
|
||||
|
||||
impl OverrideConfig {
|
||||
@@ -76,12 +79,20 @@ impl OverrideConfig {
|
||||
.with_optional(
|
||||
Config::with_enabled_network_requester,
|
||||
self.with_network_requester,
|
||||
)
|
||||
.with_optional(
|
||||
Config::with_enabled_ip_packet_router,
|
||||
self.with_ip_packet_router,
|
||||
);
|
||||
|
||||
if config.network_requester.enabled
|
||||
&& config.storage_paths.network_requester_config.is_none()
|
||||
{
|
||||
Ok(config.with_default_network_requester_config_path())
|
||||
} else if config.ip_packet_router.enabled
|
||||
&& config.storage_paths.ip_packet_router_config.is_none()
|
||||
{
|
||||
Ok(config.with_default_ip_packet_router_config_path())
|
||||
} else {
|
||||
Ok(config)
|
||||
}
|
||||
@@ -101,6 +112,11 @@ pub(crate) struct OverrideNetworkRequesterConfig {
|
||||
pub(crate) statistics_recipient: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct OverrideIpPacketRouterConfig {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// Ensures that a given bech32 address is valid
|
||||
pub(crate) fn ensure_correct_bech32_prefix(address: &AccountId) -> Result<(), GatewayError> {
|
||||
let expected_prefix =
|
||||
@@ -161,6 +177,10 @@ fn make_nr_id(gateway_id: &str) -> String {
|
||||
format!("{gateway_id}-network-requester")
|
||||
}
|
||||
|
||||
fn make_ip_id(gateway_id: &str) -> String {
|
||||
format!("{gateway_id}-ip-packet-router")
|
||||
}
|
||||
|
||||
// NOTE: make sure this is in sync with service-providers/network-requester/src/cli/mod.rs::override_config
|
||||
pub(crate) fn override_network_requester_config(
|
||||
mut cfg: nym_network_requester::Config,
|
||||
@@ -220,6 +240,13 @@ pub(crate) fn override_network_requester_config(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn override_ip_packet_router_config(
|
||||
cfg: nym_ip_packet_router::Config,
|
||||
_opts: Option<OverrideIpPacketRouterConfig>,
|
||||
) -> nym_ip_packet_router::Config {
|
||||
cfg
|
||||
}
|
||||
|
||||
pub(crate) async fn initialise_local_network_requester(
|
||||
gateway_config: &Config,
|
||||
opts: OverrideNetworkRequesterConfig,
|
||||
@@ -275,6 +302,7 @@ pub(crate) async fn initialise_local_network_requester(
|
||||
enabled: gateway_config.network_requester.enabled,
|
||||
identity_key: address.identity().to_string(),
|
||||
encryption_key: address.encryption_key().to_string(),
|
||||
exit_policy: !nr_cfg.network_requester.use_deprecated_allow_list,
|
||||
open_proxy: nr_cfg.network_requester.open_proxy,
|
||||
enabled_statistics: nr_cfg.network_requester.enabled_statistics,
|
||||
address: address.to_string(),
|
||||
@@ -291,3 +319,63 @@ pub(crate) async fn initialise_local_network_requester(
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn initialise_local_ip_packet_router(
|
||||
gateway_config: &Config,
|
||||
opts: OverrideIpPacketRouterConfig,
|
||||
identity: identity::PublicKey,
|
||||
) -> Result<GatewayIpPacketRouterDetails, GatewayError> {
|
||||
info!("initialising ip packet router...");
|
||||
let Some(ip_cfg_path) = gateway_config.storage_paths.ip_packet_router_config() else {
|
||||
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
|
||||
};
|
||||
|
||||
let id = &gateway_config.gateway.id;
|
||||
let ip_id = make_ip_id(id);
|
||||
let ip_data_dir = default_ip_packet_router_data_dir(id);
|
||||
let mut ip_cfg = nym_ip_packet_router::Config::new(&ip_id).with_data_directory(ip_data_dir);
|
||||
ip_cfg = override_ip_packet_router_config(ip_cfg, Some(opts));
|
||||
|
||||
let key_store = OnDiskKeys::new(ip_cfg.storage_paths.common_paths.keys.clone());
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&ip_cfg.storage_paths.common_paths.gateway_details);
|
||||
|
||||
// gateway setup here is way simpler as we're 'connecting' to ourselves
|
||||
let init_res = setup_gateway(
|
||||
GatewaySetup::New {
|
||||
specification: GatewaySelectionSpecification::Custom {
|
||||
gateway_identity: identity.to_base58_string(),
|
||||
additional_data: Default::default(),
|
||||
},
|
||||
available_gateways: vec![],
|
||||
overwrite_data: false,
|
||||
},
|
||||
&key_store,
|
||||
&details_store,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let address = init_res.client_address()?;
|
||||
|
||||
if let Err(err) = save_formatted_config_to_file(&ip_cfg, ip_cfg_path) {
|
||||
log::error!("Failed to save the ip packet router config file: {err}");
|
||||
return Err(GatewayError::ConfigSaveFailure {
|
||||
id: ip_id,
|
||||
path: ip_cfg_path.to_path_buf(),
|
||||
source: err,
|
||||
});
|
||||
} else {
|
||||
eprintln!(
|
||||
"Saved ip packet router configuration file to {}",
|
||||
ip_cfg_path.display()
|
||||
)
|
||||
}
|
||||
|
||||
Ok(GatewayIpPacketRouterDetails {
|
||||
enabled: gateway_config.ip_packet_router.enabled,
|
||||
identity_key: address.identity().to_string(),
|
||||
encryption_key: address.encryption_key().to_string(),
|
||||
address: address.to_string(),
|
||||
config_path: ip_cfg_path.display().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::helpers::{
|
||||
initialise_local_network_requester, OverrideNetworkRequesterConfig,
|
||||
initialise_local_ip_packet_router, initialise_local_network_requester,
|
||||
OverrideNetworkRequesterConfig,
|
||||
};
|
||||
use crate::config::{default_config_directory, default_config_filepath, default_data_directory};
|
||||
use crate::node::helpers::node_details;
|
||||
@@ -14,6 +15,8 @@ use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
|
||||
use super::helpers::OverrideIpPacketRouterConfig;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub struct Init {
|
||||
/// Id of the gateway we want to create config for
|
||||
@@ -79,9 +82,13 @@ pub struct Init {
|
||||
statistics_service_url: Option<url::Url>,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[clap(long)]
|
||||
#[clap(long, conflicts_with = "with_ip_packet_router")]
|
||||
with_network_requester: bool,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[clap(long, hide = true, conflicts_with = "with_network_requester")]
|
||||
with_ip_packet_router: bool,
|
||||
|
||||
// ##### NETWORK REQUESTER FLAGS #####
|
||||
/// Specifies whether this network requester should run in 'open-proxy' mode
|
||||
#[clap(long, requires = "with_network_requester")]
|
||||
@@ -154,6 +161,7 @@ impl From<Init> for OverrideConfig {
|
||||
nyxd_urls: init_config.nyxd_urls,
|
||||
only_coconut_credentials: init_config.only_coconut_credentials,
|
||||
with_network_requester: Some(init_config.with_network_requester),
|
||||
with_ip_packet_router: Some(init_config.with_ip_packet_router),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,6 +180,12 @@ impl<'a> From<&'a Init> for OverrideNetworkRequesterConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Init> for OverrideIpPacketRouterConfig {
|
||||
fn from(_value: &Init) -> Self {
|
||||
OverrideIpPacketRouterConfig {}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_paths(id: &str) -> io::Result<()> {
|
||||
fs::create_dir_all(default_data_directory(id))?;
|
||||
fs::create_dir_all(default_config_directory(id))
|
||||
@@ -196,6 +210,7 @@ pub async fn execute(args: Init) -> anyhow::Result<()> {
|
||||
// Initialising the config structure is just overriding a default constructed one
|
||||
let fresh_config = Config::new(&args.id);
|
||||
let nr_opts = (&args).into();
|
||||
let ip_opts = (&args).into();
|
||||
let mut config = OverrideConfig::from(args).do_override(fresh_config)?;
|
||||
|
||||
// if gateway was already initialised, don't generate new keys, et al.
|
||||
@@ -228,6 +243,9 @@ pub async fn execute(args: Init) -> anyhow::Result<()> {
|
||||
if config.network_requester.enabled {
|
||||
initialise_local_network_requester(&config, nr_opts, *identity_keys.public_key())
|
||||
.await?;
|
||||
} else if config.ip_packet_router.enabled {
|
||||
initialise_local_ip_packet_router(&config, ip_opts, *identity_keys.public_key())
|
||||
.await?;
|
||||
}
|
||||
|
||||
eprintln!("Saved identity and mixnet sphinx keypairs");
|
||||
@@ -276,6 +294,7 @@ mod tests {
|
||||
only_coconut_credentials: None,
|
||||
output: Default::default(),
|
||||
with_network_requester: false,
|
||||
with_ip_packet_router: false,
|
||||
open_proxy: None,
|
||||
enable_statistics: None,
|
||||
statistics_recipient: None,
|
||||
@@ -303,6 +322,7 @@ mod tests {
|
||||
let _gateway = Gateway::new_from_keys_and_storage(
|
||||
config,
|
||||
None,
|
||||
None,
|
||||
identity_keys,
|
||||
sphinx_keys,
|
||||
InMemStorage,
|
||||
|
||||
@@ -12,6 +12,7 @@ pub(crate) mod helpers;
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod node_details;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod setup_ip_packet_router;
|
||||
pub(crate) mod setup_network_requester;
|
||||
pub(crate) mod sign;
|
||||
mod upgrade_helpers;
|
||||
@@ -31,6 +32,11 @@ pub(crate) enum Commands {
|
||||
// essentially an option to include NR without having to setup fresh gateway
|
||||
SetupNetworkRequester(setup_network_requester::CmdArgs),
|
||||
|
||||
/// Add ip packet router support to this gateway
|
||||
// essentially an option to include ip packet router without having to setup fresh gateway
|
||||
#[command(hide = true)]
|
||||
SetupIpPacketRouter(setup_ip_packet_router::CmdArgs),
|
||||
|
||||
/// Sign text to prove ownership of this mixnode
|
||||
Sign(sign::Sign),
|
||||
|
||||
@@ -52,6 +58,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
|
||||
Commands::NodeDetails(m) => node_details::execute(m).await?,
|
||||
Commands::Run(m) => run::execute(m).await?,
|
||||
Commands::SetupNetworkRequester(m) => setup_network_requester::execute(m).await?,
|
||||
Commands::SetupIpPacketRouter(m) => setup_ip_packet_router::execute(m).await?,
|
||||
Commands::Sign(m) => sign::execute(m)?,
|
||||
Commands::BuildInfo(m) => build_info::execute(m),
|
||||
Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name),
|
||||
|
||||
+34
-19
@@ -15,39 +15,41 @@ use nym_node::error::NymNodeError;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::helpers::OverrideIpPacketRouterConfig;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub struct Run {
|
||||
/// Id of the gateway we want to run
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
id: String,
|
||||
|
||||
/// The custom listening address on which the gateway will be running for receiving sphinx packets
|
||||
#[clap(long, alias = "host")]
|
||||
#[arg(long, alias = "host")]
|
||||
listening_address: Option<IpAddr>,
|
||||
|
||||
/// Comma separated list of public ip addresses that will announced to the nym-api and subsequently to the clients.
|
||||
/// In nearly all circumstances, it's going to be identical to the address you're going to use for bonding.
|
||||
#[clap(long, value_delimiter = ',')]
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
public_ips: Option<Vec<IpAddr>>,
|
||||
|
||||
/// Optional hostname associated with this gateway that will announced to the nym-api and subsequently to the clients
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
hostname: Option<String>,
|
||||
|
||||
/// The port on which the gateway will be listening for sphinx packets
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
mix_port: Option<u16>,
|
||||
|
||||
/// The port on which the gateway will be listening for clients gateway-requests
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
clients_port: Option<u16>,
|
||||
|
||||
/// Path to sqlite database containing all gateway persistent data
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
datastore: Option<PathBuf>,
|
||||
|
||||
/// Comma separated list of endpoints of nym APIs
|
||||
#[clap(
|
||||
#[arg(
|
||||
long,
|
||||
alias = "validator_apis",
|
||||
value_delimiter = ',',
|
||||
@@ -57,7 +59,7 @@ pub struct Run {
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of endpoints of the validator
|
||||
#[clap(
|
||||
#[arg(
|
||||
long,
|
||||
alias = "validators",
|
||||
alias = "nyxd_validators",
|
||||
@@ -68,26 +70,30 @@ pub struct Run {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Cosmos wallet mnemonic
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
mnemonic: Option<bip39::Mnemonic>,
|
||||
|
||||
/// Set this gateway to work only with coconut credentials; that would disallow clients to
|
||||
/// bypass bandwidth credential requirement
|
||||
#[clap(long, hide = true)]
|
||||
#[arg(long, hide = true)]
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
enabled_statistics: Option<bool>,
|
||||
|
||||
/// URL where a statistics aggregator is running. The default value is a Nym aggregator server
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
statistics_service_url: Option<url::Url>,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[clap(long)]
|
||||
#[arg(long, conflicts_with = "with_ip_packet_router")]
|
||||
with_network_requester: Option<bool>,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[arg(long, hide = true, conflicts_with = "with_network_requester")]
|
||||
with_ip_packet_router: Option<bool>,
|
||||
|
||||
// ##### NETWORK REQUESTER FLAGS #####
|
||||
/// Specifies whether this network requester should run in 'open-proxy' mode
|
||||
#[arg(long)]
|
||||
@@ -123,20 +129,20 @@ pub struct Run {
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
/// Only usable when local network requester is enabled.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
#[arg(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Specifies whether this network requester will run using the default ExitPolicy
|
||||
/// as opposed to the allow list.
|
||||
/// Note: this setting will become the default in the future releases.
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
with_exit_policy: Option<bool>,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
#[arg(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
|
||||
/// Flag specifying this node will be running in a local setting.
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
local: bool,
|
||||
}
|
||||
|
||||
@@ -157,6 +163,7 @@ impl From<Run> for OverrideConfig {
|
||||
nyxd_urls: run_config.nyxd_urls,
|
||||
only_coconut_credentials: run_config.only_coconut_credentials,
|
||||
with_network_requester: run_config.with_network_requester,
|
||||
with_ip_packet_router: run_config.with_ip_packet_router,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +182,12 @@ impl<'a> From<&'a Run> for OverrideNetworkRequesterConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Run> for OverrideIpPacketRouterConfig {
|
||||
fn from(_value: &Run) -> Self {
|
||||
OverrideIpPacketRouterConfig {}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_binding_warning(address: IpAddr) {
|
||||
eprintln!("\n##### NOTE #####");
|
||||
eprintln!(
|
||||
@@ -217,6 +230,7 @@ pub async fn execute(args: Run) -> anyhow::Result<()> {
|
||||
let output = args.output;
|
||||
let custom_mixnet = args.custom_mixnet.clone();
|
||||
let nr_opts = (&args).into();
|
||||
let ip_opts = (&args).into();
|
||||
|
||||
let config = build_config(id, args)?;
|
||||
ensure_config_version_compatibility(&config)?;
|
||||
@@ -235,7 +249,8 @@ pub async fn execute(args: Run) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let node_details = node_details(&config)?;
|
||||
let gateway = crate::node::create_gateway(config, Some(nr_opts), custom_mixnet).await?;
|
||||
let gateway =
|
||||
crate::node::create_gateway(config, Some(nr_opts), Some(ip_opts), custom_mixnet).await?;
|
||||
eprintln!(
|
||||
"\nTo bond your gateway you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\
|
||||
Select the correct version and install it to your machine. You will need to provide some of the following: \n ");
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::helpers::{
|
||||
initialise_local_ip_packet_router, try_load_current_config, OverrideIpPacketRouterConfig,
|
||||
};
|
||||
use crate::node::helpers::load_public_key;
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub struct CmdArgs {
|
||||
/// The id of the gateway you want to initialise local ip packet router for.
|
||||
#[arg(long)]
|
||||
id: String,
|
||||
|
||||
/// Path to custom location for ip packet routers' config.
|
||||
#[arg(long)]
|
||||
custom_config_path: Option<PathBuf>,
|
||||
|
||||
/// Specify whether the ip packet router should be enabled.
|
||||
// (you might want to create all the configs, generate keys, etc. but not actually run the NR just yet)
|
||||
#[arg(long)]
|
||||
enabled: Option<bool>,
|
||||
|
||||
#[arg(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
|
||||
impl From<&CmdArgs> for OverrideIpPacketRouterConfig {
|
||||
fn from(_value: &CmdArgs) -> Self {
|
||||
OverrideIpPacketRouterConfig {}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(args: CmdArgs) -> anyhow::Result<()> {
|
||||
let mut config = try_load_current_config(&args.id)?;
|
||||
let opts = (&args).into();
|
||||
|
||||
// if somebody provided config file of a custom NR, that's fine
|
||||
// but in 90% cases, I'd assume, it won't work due to invalid gateway configuration
|
||||
// but it might be nice to be able to move files around.
|
||||
if let Some(custom_config_path) = args.custom_config_path {
|
||||
// if you specified anything as the argument, overwrite whatever was already in the config file
|
||||
config.storage_paths.ip_packet_router_config = Some(custom_config_path);
|
||||
}
|
||||
|
||||
if let Some(override_enabled) = args.enabled {
|
||||
config.ip_packet_router.enabled = override_enabled;
|
||||
}
|
||||
|
||||
if config.storage_paths.ip_packet_router_config.is_none() {
|
||||
config = config.with_default_ip_packet_router_config_path()
|
||||
}
|
||||
|
||||
let identity_public_key = load_public_key(
|
||||
&config.storage_paths.keys.public_identity_key_file,
|
||||
"gateway identity",
|
||||
)?;
|
||||
let details = initialise_local_ip_packet_router(&config, opts, identity_public_key).await?;
|
||||
config.try_save()?;
|
||||
|
||||
args.output.to_stdout(&details);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
|
||||
use crate::config::old_config_v1_1_28::ConfigV1_1_28;
|
||||
use crate::config::old_config_v1_1_29::ConfigV1_1_29;
|
||||
use crate::config::old_config_v1_1_31::ConfigV1_1_31;
|
||||
use crate::config::{default_config_filepath, Config};
|
||||
use crate::error::GatewayError;
|
||||
use log::info;
|
||||
@@ -22,7 +23,8 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
|
||||
|
||||
let updated_step1: ConfigV1_1_28 = old_config.into();
|
||||
let updated_step2: ConfigV1_1_29 = updated_step1.into();
|
||||
let updated: Config = updated_step2.into();
|
||||
let updated_step3: ConfigV1_1_31 = updated_step2.into();
|
||||
let updated: Config = updated_step3.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
@@ -45,7 +47,8 @@ fn try_upgrade_v1_1_28_config(id: &str) -> Result<bool, GatewayError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_29 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
let updated_step2: ConfigV1_1_31 = updated_step1.into();
|
||||
let updated: Config = updated_step2.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
@@ -67,6 +70,29 @@ fn try_upgrade_v1_1_29_config(id: &str) -> Result<bool, GatewayError> {
|
||||
info!("It seems the gateway is using <= v1.1.29 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_31 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
path: default_config_filepath(id),
|
||||
id: id.to_string(),
|
||||
source: err,
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_31_config(id: &str) -> Result<bool, GatewayError> {
|
||||
// explicitly load it as v1.1.30 (which is incompatible with the current, i.e. 1.1.31+)
|
||||
let Ok(old_config) = ConfigV1_1_31::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the gateway is using <= v1.1.30 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
@@ -89,6 +115,9 @@ pub(crate) fn try_upgrade_config(id: &str) -> Result<(), GatewayError> {
|
||||
if try_upgrade_v1_1_29_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_31_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
pub(crate) mod old_config_v1_1_20;
|
||||
pub(crate) mod old_config_v1_1_28;
|
||||
pub(crate) mod old_config_v1_1_29;
|
||||
pub(crate) mod old_config_v1_1_31;
|
||||
pub mod persistence;
|
||||
mod template;
|
||||
|
||||
@@ -100,6 +101,9 @@ pub struct Config {
|
||||
|
||||
pub network_requester: NetworkRequester,
|
||||
|
||||
#[serde(default)]
|
||||
pub ip_packet_router: IpPacketRouter,
|
||||
|
||||
#[serde(default)]
|
||||
pub logging: LoggingSettings,
|
||||
|
||||
@@ -128,6 +132,7 @@ impl Config {
|
||||
wireguard: Default::default(),
|
||||
storage_paths: GatewayPaths::new_default(id.as_ref()),
|
||||
network_requester: Default::default(),
|
||||
ip_packet_router: Default::default(),
|
||||
logging: Default::default(),
|
||||
debug: Default::default(),
|
||||
}
|
||||
@@ -195,6 +200,18 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_enabled_ip_packet_router(mut self, enabled_ip_packet_router: bool) -> Self {
|
||||
self.ip_packet_router.enabled = enabled_ip_packet_router;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_default_ip_packet_router_config_path(mut self) -> Self {
|
||||
self.storage_paths = self
|
||||
.storage_paths
|
||||
.with_default_ip_packet_router_config(&self.gateway.id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_only_coconut_credentials(mut self, only_coconut_credentials: bool) -> Self {
|
||||
self.gateway.only_coconut_credentials = only_coconut_credentials;
|
||||
self
|
||||
@@ -360,6 +377,20 @@ impl Default for NetworkRequester {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct IpPacketRouter {
|
||||
/// Specifies whether ip packet router service is enabled in this process.
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for IpPacketRouter {
|
||||
fn default() -> Self {
|
||||
Self { enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Debug {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::{GatewayPaths, KeysPaths};
|
||||
use crate::config::{Config, Debug, Gateway, NetworkRequester};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
|
||||
};
|
||||
@@ -14,6 +11,11 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
use super::old_config_v1_1_31::{
|
||||
ConfigV1_1_31, DebugV1_1_31, GatewayPathsV1_1_31, GatewayV1_1_31, KeysPathsV1_1_31,
|
||||
LoggingSettingsV1_1_31, NetworkRequesterV1_1_31,
|
||||
};
|
||||
|
||||
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
|
||||
|
||||
// 'DEBUG'
|
||||
@@ -105,9 +107,9 @@ impl ConfigV1_1_29 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_29> for Config {
|
||||
impl From<ConfigV1_1_29> for ConfigV1_1_31 {
|
||||
fn from(value: ConfigV1_1_29) -> Self {
|
||||
Config {
|
||||
ConfigV1_1_31 {
|
||||
save_path: value.save_path,
|
||||
|
||||
// \/ ADDED
|
||||
@@ -121,7 +123,7 @@ impl From<ConfigV1_1_29> for Config {
|
||||
// \/ ADDED
|
||||
http: Default::default(),
|
||||
// /\ ADDED
|
||||
gateway: Gateway {
|
||||
gateway: GatewayV1_1_31 {
|
||||
version: value.gateway.version,
|
||||
id: value.gateway.id,
|
||||
only_coconut_credentials: value.gateway.only_coconut_credentials,
|
||||
@@ -141,8 +143,8 @@ impl From<ConfigV1_1_29> for Config {
|
||||
// \/ ADDED
|
||||
wireguard: Default::default(),
|
||||
// /\ ADDED
|
||||
storage_paths: GatewayPaths {
|
||||
keys: KeysPaths {
|
||||
storage_paths: GatewayPathsV1_1_31 {
|
||||
keys: KeysPathsV1_1_31 {
|
||||
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
|
||||
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
|
||||
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
|
||||
@@ -151,11 +153,11 @@ impl From<ConfigV1_1_29> for Config {
|
||||
clients_storage: value.storage_paths.clients_storage,
|
||||
network_requester_config: value.storage_paths.network_requester_config,
|
||||
},
|
||||
network_requester: NetworkRequester {
|
||||
network_requester: NetworkRequesterV1_1_31 {
|
||||
enabled: value.network_requester.enabled,
|
||||
},
|
||||
logging: LoggingSettings {},
|
||||
debug: Debug {
|
||||
logging: LoggingSettingsV1_1_31 {},
|
||||
debug: DebugV1_1_31 {
|
||||
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.debug.initial_connection_timeout,
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::GatewayPaths;
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
use super::persistence::paths::KeysPaths;
|
||||
use super::{Config, Debug, Gateway, NetworkRequester};
|
||||
|
||||
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
|
||||
|
||||
// 'DEBUG'
|
||||
// where applicable, the below are defined in milliseconds
|
||||
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
|
||||
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
|
||||
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
|
||||
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
|
||||
|
||||
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
|
||||
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
|
||||
|
||||
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let port = u16::deserialize(deserializer)?;
|
||||
if port == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(port))
|
||||
}
|
||||
}
|
||||
|
||||
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let path = PathBuf::deserialize(deserializer)?;
|
||||
if path.as_os_str().is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(path))
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive default path to gateway's config directory.
|
||||
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
|
||||
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
must_get_home()
|
||||
.join(NYM_DIR)
|
||||
.join(DEFAULT_GATEWAYS_DIR)
|
||||
.join(id)
|
||||
.join(DEFAULT_CONFIG_DIR)
|
||||
}
|
||||
|
||||
/// Derive default path to gateways's config file.
|
||||
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
|
||||
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_31 {
|
||||
// additional metadata holding on-disk location of this config file
|
||||
#[serde(skip)]
|
||||
pub(crate) save_path: Option<PathBuf>,
|
||||
|
||||
pub host: nym_node::config::Host,
|
||||
|
||||
#[serde(default)]
|
||||
pub http: nym_node::config::Http,
|
||||
|
||||
pub gateway: GatewayV1_1_31,
|
||||
|
||||
#[serde(default)]
|
||||
// currently not really used for anything useful
|
||||
pub wireguard: WireguardV1_1_31,
|
||||
|
||||
pub storage_paths: GatewayPathsV1_1_31,
|
||||
|
||||
pub network_requester: NetworkRequesterV1_1_31,
|
||||
|
||||
#[serde(default)]
|
||||
pub logging: LoggingSettingsV1_1_31,
|
||||
|
||||
#[serde(default)]
|
||||
pub debug: DebugV1_1_31,
|
||||
}
|
||||
|
||||
impl ConfigV1_1_31 {
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_31> for Config {
|
||||
fn from(value: ConfigV1_1_31) -> Self {
|
||||
Self {
|
||||
save_path: value.save_path,
|
||||
host: value.host,
|
||||
http: value.http,
|
||||
gateway: Gateway {
|
||||
version: value.gateway.version,
|
||||
id: value.gateway.id,
|
||||
only_coconut_credentials: value.gateway.only_coconut_credentials,
|
||||
listening_address: value.gateway.listening_address,
|
||||
mix_port: value.gateway.mix_port,
|
||||
clients_port: value.gateway.clients_port,
|
||||
clients_wss_port: value.gateway.clients_wss_port,
|
||||
enabled_statistics: value.gateway.enabled_statistics,
|
||||
statistics_service_url: value.gateway.statistics_service_url,
|
||||
nym_api_urls: value.gateway.nym_api_urls,
|
||||
nyxd_urls: value.gateway.nyxd_urls,
|
||||
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
|
||||
},
|
||||
wireguard: nym_node::config::Wireguard {
|
||||
enabled: value.wireguard.enabled,
|
||||
bind_address: value.wireguard.bind_address,
|
||||
announced_port: value.wireguard.announced_port,
|
||||
storage_paths: nym_node::config::persistence::WireguardPaths {
|
||||
// no fields (yet)
|
||||
},
|
||||
},
|
||||
storage_paths: GatewayPaths {
|
||||
keys: KeysPaths {
|
||||
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
|
||||
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
|
||||
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
|
||||
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
|
||||
},
|
||||
clients_storage: value.storage_paths.clients_storage,
|
||||
network_requester_config: value.storage_paths.network_requester_config,
|
||||
// \/ ADDED
|
||||
ip_packet_router_config: Default::default(),
|
||||
// /\ ADDED
|
||||
},
|
||||
network_requester: NetworkRequester {
|
||||
enabled: value.network_requester.enabled,
|
||||
},
|
||||
// \/ ADDED
|
||||
ip_packet_router: Default::default(),
|
||||
// /\ ADDED
|
||||
logging: LoggingSettings {
|
||||
// no fields (yet)
|
||||
},
|
||||
debug: Debug {
|
||||
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.debug.initial_connection_timeout,
|
||||
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
|
||||
presence_sending_delay: value.debug.presence_sending_delay,
|
||||
stored_messages_filename_length: value.debug.stored_messages_filename_length,
|
||||
message_retrieval_limit: value.debug.message_retrieval_limit,
|
||||
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct GatewayV1_1_31 {
|
||||
/// Version of the gateway for which this configuration was created.
|
||||
pub version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular gateway.
|
||||
pub id: String,
|
||||
|
||||
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
|
||||
/// the mixnet, or if it also accepts non-paying clients
|
||||
#[serde(default)]
|
||||
pub only_coconut_credentials: bool,
|
||||
|
||||
/// Address to which this mixnode will bind to and will be listening for packets.
|
||||
pub listening_address: IpAddr,
|
||||
|
||||
/// Port used for listening for all mixnet traffic.
|
||||
/// (default: 1789)
|
||||
pub mix_port: u16,
|
||||
|
||||
/// Port used for listening for all client-related traffic.
|
||||
/// (default: 9000)
|
||||
pub clients_port: u16,
|
||||
|
||||
/// If applicable, announced port for listening for secure websocket client traffic.
|
||||
/// (default: None)
|
||||
#[serde(deserialize_with = "de_maybe_port")]
|
||||
pub clients_wss_port: Option<u16>,
|
||||
|
||||
/// Whether gateway collects and sends anonymized statistics
|
||||
pub enabled_statistics: bool,
|
||||
|
||||
/// Domain address of the statistics service
|
||||
pub statistics_service_url: Url,
|
||||
|
||||
/// Addresses to APIs from which the node gets the view of the network.
|
||||
#[serde(alias = "validator_api_urls")]
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
|
||||
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
|
||||
#[serde(alias = "validator_nymd_urls")]
|
||||
pub nyxd_urls: Vec<Url>,
|
||||
|
||||
/// Mnemonic of a cosmos wallet used in checking for double spending.
|
||||
// #[deprecated(note = "move to storage")]
|
||||
// TODO: I don't think this should be stored directly in the config...
|
||||
pub cosmos_mnemonic: bip39::Mnemonic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct WireguardV1_1_31 {
|
||||
/// Specifies whether the wireguard service is enabled on this node.
|
||||
pub enabled: bool,
|
||||
|
||||
/// Socket address this node will use for binding its wireguard interface.
|
||||
/// default: `0.0.0.0:51820`
|
||||
pub bind_address: SocketAddr,
|
||||
|
||||
/// Port announced to external clients wishing to connect to the wireguard interface.
|
||||
/// Useful in the instances where the node is behind a proxy.
|
||||
pub announced_port: u16,
|
||||
|
||||
/// Paths for wireguard keys, client registries, etc.
|
||||
pub storage_paths: WireguardPathsV1_1_31,
|
||||
}
|
||||
|
||||
impl Default for WireguardV1_1_31 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
bind_address: SocketAddr::new(
|
||||
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
nym_node::config::DEFAULT_WIREGUARD_PORT,
|
||||
),
|
||||
announced_port: nym_node::config::DEFAULT_WIREGUARD_PORT,
|
||||
storage_paths: WireguardPathsV1_1_31 {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct WireguardPathsV1_1_31 {
|
||||
// pub keys:
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GatewayPathsV1_1_31 {
|
||||
pub keys: KeysPathsV1_1_31,
|
||||
|
||||
/// Path to sqlite database containing all persistent data: messages for offline clients,
|
||||
/// derived shared keys and available client bandwidths.
|
||||
#[serde(alias = "persistent_storage")]
|
||||
pub clients_storage: PathBuf,
|
||||
|
||||
/// Path to the configuration of the embedded network requester.
|
||||
#[serde(deserialize_with = "de_maybe_path")]
|
||||
pub network_requester_config: Option<PathBuf>,
|
||||
// pub node_description: PathBuf,
|
||||
|
||||
// pub cosmos_bip39_mnemonic: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct KeysPathsV1_1_31 {
|
||||
/// Path to file containing private identity key.
|
||||
pub private_identity_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing public identity key.
|
||||
pub public_identity_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing private sphinx key.
|
||||
pub private_sphinx_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing public sphinx key.
|
||||
pub public_sphinx_key_file: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct NetworkRequesterV1_1_31 {
|
||||
/// Specifies whether network requester service is enabled in this process.
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for NetworkRequesterV1_1_31 {
|
||||
fn default() -> Self {
|
||||
Self { enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LoggingSettingsV1_1_31 {
|
||||
// well, we need to implement something here at some point...
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DebugV1_1_31 {
|
||||
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_forwarding_initial_backoff: Duration,
|
||||
|
||||
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_forwarding_maximum_backoff: Duration,
|
||||
|
||||
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub initial_connection_timeout: Duration,
|
||||
|
||||
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
|
||||
pub maximum_connection_buffer_size: usize,
|
||||
|
||||
/// Delay between each subsequent presence data being sent.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub presence_sending_delay: Duration,
|
||||
|
||||
/// Length of filenames for new client messages.
|
||||
pub stored_messages_filename_length: u16,
|
||||
|
||||
/// Number of messages from offline client that can be pulled at once from the storage.
|
||||
pub message_retrieval_limit: i64,
|
||||
|
||||
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
|
||||
// it's set to true by default. The reason for that decision is to preserve compatibility with the
|
||||
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
|
||||
// It shall be disabled in the subsequent releases.
|
||||
pub use_legacy_framed_packet_version: bool,
|
||||
}
|
||||
|
||||
impl Default for DebugV1_1_31 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
|
||||
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
|
||||
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
|
||||
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
|
||||
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
|
||||
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
|
||||
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
|
||||
use_legacy_framed_packet_version: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,19 @@ pub const DEFAULT_CLIENTS_STORAGE_FILENAME: &str = "db.sqlite";
|
||||
pub const DEFAULT_NETWORK_REQUESTER_CONFIG_FILENAME: &str = "network_requester_config.toml";
|
||||
pub const DEFAULT_NETWORK_REQUESTER_DATA_DIR: &str = "network-requester-data";
|
||||
|
||||
pub const DEFAULT_IP_PACKET_ROUTER_CONFIG_FILENAME: &str = "ip_packet_router_config.toml";
|
||||
pub const DEFAULT_IP_PACKET_ROUTER_DATA_DIR: &str = "ip-packet-router-data";
|
||||
|
||||
// pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml";
|
||||
|
||||
pub fn default_network_requester_data_dir<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
default_data_directory(id).join(DEFAULT_NETWORK_REQUESTER_DATA_DIR)
|
||||
}
|
||||
|
||||
pub fn default_ip_packet_router_data_dir<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
default_data_directory(id).join(DEFAULT_IP_PACKET_ROUTER_DATA_DIR)
|
||||
}
|
||||
|
||||
/// makes sure that an empty path is converted into a `None` as opposed to `Some("")`
|
||||
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
|
||||
where
|
||||
@@ -50,6 +57,9 @@ pub struct GatewayPaths {
|
||||
// pub node_description: PathBuf,
|
||||
|
||||
// pub cosmos_bip39_mnemonic: PathBuf,
|
||||
/// Path to the configuration of the embedded ip packet router.
|
||||
#[serde(deserialize_with = "de_maybe_path")]
|
||||
pub ip_packet_router_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl GatewayPaths {
|
||||
@@ -59,6 +69,7 @@ impl GatewayPaths {
|
||||
clients_storage: default_data_directory(id).join(DEFAULT_CLIENTS_STORAGE_FILENAME),
|
||||
// node_description: default_config_filepath(id).join(DEFAULT_DESCRIPTION_FILENAME),
|
||||
network_requester_config: None,
|
||||
ip_packet_router_config: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +86,27 @@ impl GatewayPaths {
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_ip_packet_router_config<P: AsRef<Path>>(mut self, path: P) -> Self {
|
||||
self.ip_packet_router_config = Some(path.as_ref().into());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_default_ip_packet_router_config<P: AsRef<Path>>(self, id: P) -> Self {
|
||||
self.with_ip_packet_router_config(
|
||||
default_config_directory(id).join(DEFAULT_IP_PACKET_ROUTER_CONFIG_FILENAME),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn network_requester_config(&self) -> &Option<PathBuf> {
|
||||
&self.network_requester_config
|
||||
}
|
||||
|
||||
pub fn ip_packet_router_config(&self) -> &Option<PathBuf> {
|
||||
&self.ip_packet_router_config
|
||||
}
|
||||
|
||||
pub fn private_identity_key(&self) -> &Path {
|
||||
self.keys.private_identity_key()
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ landing_page_assets_path = '{{ http.landing_page_assets_path }}'
|
||||
# Specifies whether network requester service is enabled in this process.
|
||||
enabled = {{ network_requester.enabled }}
|
||||
|
||||
[ip_packet_router]
|
||||
# Specifies whether ip packet router service is enabled in this process.
|
||||
enabled = {{ ip_packet_router.enabled }}
|
||||
|
||||
[storage_paths]
|
||||
|
||||
# Path to file containing private identity key.
|
||||
@@ -103,6 +107,9 @@ clients_storage = '{{ storage_paths.clients_storage }}'
|
||||
# Path to the configuration of the embedded network requester.
|
||||
network_requester_config = '{{ storage_paths.network_requester_config }}'
|
||||
|
||||
# Path to the configuration of the embedded ip packet router.
|
||||
ip_packet_router_config = '{{ storage_paths.ip_packet_router_config }}'
|
||||
|
||||
##### logging configuration options #####
|
||||
|
||||
[logging]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node::storage::error::StorageError;
|
||||
use nym_ip_packet_router::error::IpForwarderError;
|
||||
use nym_network_requester::error::{ClientCoreError, NetworkRequesterError};
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
@@ -48,6 +49,17 @@ pub(crate) enum GatewayError {
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"failed to load config file for ip packet router (gateway-id: '{id}') using path '{}'. detailed message: {source}",
|
||||
path.display()
|
||||
)]
|
||||
IpPacketRouterConfigLoadFailure {
|
||||
id: String,
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"failed to save config file for id {id} using path '{}'. detailed message: {source}", path.display()
|
||||
)]
|
||||
@@ -86,15 +98,27 @@ pub(crate) enum GatewayError {
|
||||
#[error("Path to network requester configuration file hasn't been specified. Perhaps try to run `setup-network-requester`?")]
|
||||
UnspecifiedNetworkRequesterConfig,
|
||||
|
||||
#[error("Path to ip packet router configuration file hasn't been specified. Perhaps try to run `setup-ip-packet-router`?")]
|
||||
UnspecifiedIpPacketRouterConfig,
|
||||
|
||||
#[error("there was an issue with the local network requester: {source}")]
|
||||
NetworkRequesterFailure {
|
||||
#[from]
|
||||
source: NetworkRequesterError,
|
||||
},
|
||||
|
||||
#[error("there was an issue with the local ip packet router: {source}")]
|
||||
IpPacketRouterFailure {
|
||||
#[from]
|
||||
source: IpForwarderError,
|
||||
},
|
||||
|
||||
#[error("failed to startup local network requester")]
|
||||
NetworkRequesterStartupFailure,
|
||||
|
||||
#[error("failed to startup local ip packet router")]
|
||||
IpPacketRouterStartupFailure,
|
||||
|
||||
#[error("there are no nym API endpoints available")]
|
||||
NoNymApisAvailable,
|
||||
|
||||
|
||||
@@ -28,6 +28,17 @@ impl LocalNetworkRequesterHandle {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generalize this whole thing to be general. And change the name(s).
|
||||
pub(crate) fn new_ip(
|
||||
start_data: nym_ip_packet_router::OnStartData,
|
||||
mix_message_sender: MixMessageSender,
|
||||
) -> Self {
|
||||
Self {
|
||||
address: start_data.address,
|
||||
mix_message_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn client_destination(&self) -> DestinationAddressBytes {
|
||||
self.address.identity().derive_destination_address()
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ pub(crate) fn node_details(config: &Config) -> Result<GatewayNodeDetailsResponse
|
||||
identity_key: nr_identity_public_key.to_base58_string(),
|
||||
encryption_key: nr_encryption_key.to_base58_string(),
|
||||
open_proxy: cfg.network_requester.open_proxy,
|
||||
exit_policy: !cfg.network_requester.use_deprecated_allow_list,
|
||||
enabled_statistics: cfg.network_requester.enabled_statistics,
|
||||
address: address.to_string(),
|
||||
config_path: display_path(nr_cfg_path),
|
||||
@@ -96,6 +97,20 @@ pub(crate) fn load_network_requester_config<P: AsRef<Path>>(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn load_ip_packet_router_config<P: AsRef<Path>>(
|
||||
id: &str,
|
||||
path: P,
|
||||
) -> Result<nym_ip_packet_router::Config, GatewayError> {
|
||||
let path = path.as_ref();
|
||||
nym_ip_packet_router::Config::read_from_toml_file(path).map_err(|err| {
|
||||
GatewayError::IpPacketRouterConfigLoadFailure {
|
||||
id: id.to_string(),
|
||||
path: path.to_path_buf(),
|
||||
source: err,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn initialise_main_storage(
|
||||
config: &Config,
|
||||
) -> Result<PersistentStorage, GatewayError> {
|
||||
|
||||
+120
-5
@@ -1,8 +1,12 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use self::helpers::load_ip_packet_router_config;
|
||||
use self::storage::PersistentStorage;
|
||||
use crate::commands::helpers::{override_network_requester_config, OverrideNetworkRequesterConfig};
|
||||
use crate::commands::helpers::{
|
||||
override_ip_packet_router_config, override_network_requester_config,
|
||||
OverrideIpPacketRouterConfig, OverrideNetworkRequesterConfig,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
use crate::http::HttpApiBuilder;
|
||||
@@ -54,7 +58,8 @@ struct StartedNetworkRequester {
|
||||
pub(crate) async fn create_gateway(
|
||||
config: Config,
|
||||
nr_config_override: Option<OverrideNetworkRequesterConfig>,
|
||||
custom_nr_mixnet: Option<PathBuf>,
|
||||
ip_config_override: Option<OverrideIpPacketRouterConfig>,
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
) -> Result<Gateway, GatewayError> {
|
||||
// don't attempt to read config if NR is disabled
|
||||
let network_requester_config = if config.network_requester.enabled {
|
||||
@@ -69,14 +74,32 @@ pub(crate) async fn create_gateway(
|
||||
None
|
||||
};
|
||||
|
||||
// don't attempt to read config if NR is disabled
|
||||
let ip_packet_router_config = if config.ip_packet_router.enabled {
|
||||
if let Some(path) = &config.storage_paths.ip_packet_router_config {
|
||||
let cfg = load_ip_packet_router_config(&config.gateway.id, path)?;
|
||||
Some(override_ip_packet_router_config(cfg, ip_config_override))
|
||||
} else {
|
||||
// if NR is enabled, the config path must be specified
|
||||
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let storage = initialise_main_storage(&config).await?;
|
||||
|
||||
let nr_opts = network_requester_config.map(|config| LocalNetworkRequesterOpts {
|
||||
config,
|
||||
custom_mixnet_path: custom_nr_mixnet,
|
||||
config: config.clone(),
|
||||
custom_mixnet_path: custom_mixnet.clone(),
|
||||
});
|
||||
|
||||
Gateway::new(config, nr_opts, storage)
|
||||
let ip_opts = ip_packet_router_config.map(|config| LocalIpPacketRouterOpts {
|
||||
config,
|
||||
custom_mixnet_path: custom_mixnet,
|
||||
});
|
||||
|
||||
Gateway::new(config, nr_opts, ip_opts, storage)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -86,11 +109,20 @@ pub struct LocalNetworkRequesterOpts {
|
||||
custom_mixnet_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalIpPacketRouterOpts {
|
||||
config: nym_ip_packet_router::Config,
|
||||
|
||||
custom_mixnet_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub(crate) struct Gateway<St = PersistentStorage> {
|
||||
config: Config,
|
||||
|
||||
network_requester_opts: Option<LocalNetworkRequesterOpts>,
|
||||
|
||||
ip_packet_router_opts: Option<LocalIpPacketRouterOpts>,
|
||||
|
||||
/// ed25519 keypair used to assert one's identity.
|
||||
identity_keypair: Arc<identity::KeyPair>,
|
||||
|
||||
@@ -106,6 +138,7 @@ impl<St> Gateway<St> {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
network_requester_opts: Option<LocalNetworkRequesterOpts>,
|
||||
ip_packet_router_opts: Option<LocalIpPacketRouterOpts>,
|
||||
storage: St,
|
||||
) -> Result<Self, GatewayError> {
|
||||
Ok(Gateway {
|
||||
@@ -114,6 +147,7 @@ impl<St> Gateway<St> {
|
||||
sphinx_keypair: Arc::new(helpers::load_sphinx_keys(&config)?),
|
||||
config,
|
||||
network_requester_opts,
|
||||
ip_packet_router_opts,
|
||||
client_registry: Arc::new(DashMap::new()),
|
||||
})
|
||||
}
|
||||
@@ -122,6 +156,7 @@ impl<St> Gateway<St> {
|
||||
pub async fn new_from_keys_and_storage(
|
||||
config: Config,
|
||||
network_requester_opts: Option<LocalNetworkRequesterOpts>,
|
||||
ip_packet_router_opts: Option<LocalIpPacketRouterOpts>,
|
||||
identity_keypair: identity::KeyPair,
|
||||
sphinx_keypair: encryption::KeyPair,
|
||||
storage: St,
|
||||
@@ -129,6 +164,7 @@ impl<St> Gateway<St> {
|
||||
Gateway {
|
||||
config,
|
||||
network_requester_opts,
|
||||
ip_packet_router_opts,
|
||||
identity_keypair: Arc::new(identity_keypair),
|
||||
sphinx_keypair: Arc::new(sphinx_keypair),
|
||||
storage,
|
||||
@@ -283,6 +319,73 @@ impl<St> Gateway<St> {
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_ip_packet_router(
|
||||
&self,
|
||||
forwarding_channel: MixForwardingSender,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<LocalNetworkRequesterHandle, GatewayError> {
|
||||
info!("Starting IP packet provider...");
|
||||
|
||||
// if network requester is enabled, configuration file must be provided!
|
||||
let Some(ip_opts) = &self.ip_packet_router_opts else {
|
||||
log::error!("IP packet router is enabled but no configuration file was provided!");
|
||||
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
|
||||
};
|
||||
|
||||
// this gateway, whenever it has anything to send to its local NR will use fake_client_tx
|
||||
let (nr_mix_sender, nr_mix_receiver) = mpsc::unbounded();
|
||||
let router_shutdown = shutdown.fork("message_router");
|
||||
|
||||
let (router_tx, mut router_rx) = oneshot::channel();
|
||||
|
||||
let transceiver = LocalGateway::new(
|
||||
*self.identity_keypair.public_key(),
|
||||
forwarding_channel,
|
||||
router_tx,
|
||||
);
|
||||
|
||||
// TODO: well, wire it up internally to gateway traffic, shutdowns, etc.
|
||||
let (on_start_tx, on_start_rx) = oneshot::channel();
|
||||
let mut ip_builder = nym_ip_packet_router::IpForwarderBuilder::new(ip_opts.config.clone())
|
||||
.with_shutdown(shutdown)
|
||||
.with_custom_gateway_transceiver(Box::new(transceiver))
|
||||
.with_wait_for_gateway(true)
|
||||
.with_on_start(on_start_tx);
|
||||
|
||||
if let Some(custom_mixnet) = &ip_opts.custom_mixnet_path {
|
||||
ip_builder = ip_builder.with_stored_topology(custom_mixnet)?
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = ip_builder.run_service_provider().await {
|
||||
// no need to panic as we have passed a task client to the ip packet router so
|
||||
// we're most likely already in the process of shutting down
|
||||
error!("ip packet router has failed: {err}")
|
||||
}
|
||||
});
|
||||
|
||||
let start_data = on_start_rx
|
||||
.await
|
||||
.map_err(|_| GatewayError::IpPacketRouterStartupFailure)?;
|
||||
|
||||
// this should be instantaneous since the data is sent on this channel before the on start is called;
|
||||
// the failure should be impossible
|
||||
let Ok(Some(packet_router)) = router_rx.try_recv() else {
|
||||
return Err(GatewayError::IpPacketRouterStartupFailure);
|
||||
};
|
||||
|
||||
MessageRouter::new(nr_mix_receiver, packet_router).start_with_shutdown(router_shutdown);
|
||||
info!(
|
||||
"the local ip packet router is running on {}",
|
||||
start_data.address
|
||||
);
|
||||
|
||||
Ok(LocalNetworkRequesterHandle::new_ip(
|
||||
start_data,
|
||||
nr_mix_sender,
|
||||
))
|
||||
}
|
||||
|
||||
async fn wait_for_interrupt(shutdown: TaskManager) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let res = shutdown.catch_interrupt().await;
|
||||
log::info!("Stopping nym gateway");
|
||||
@@ -387,6 +490,18 @@ impl<St> Gateway<St> {
|
||||
None
|
||||
};
|
||||
|
||||
// NOTE: this is mutually exclusive with the network requester (for now). This is reflected
|
||||
// in the command line arguments as well.
|
||||
if self.config.ip_packet_router.enabled {
|
||||
let embedded_ip_sp = self
|
||||
.start_ip_packet_router(
|
||||
mix_forwarding_channel.clone(),
|
||||
shutdown.subscribe().named("ip_service_provider"),
|
||||
)
|
||||
.await?;
|
||||
active_clients_store.insert_embedded(embedded_ip_sp);
|
||||
}
|
||||
|
||||
HttpApiBuilder::new(
|
||||
&self.config,
|
||||
self.identity_keypair.as_ref(),
|
||||
|
||||
+7
-4
@@ -16,9 +16,10 @@ rust-version = "1.58.1"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
axum = { workspace = true }
|
||||
anyhow = "1.0.40"
|
||||
bs58 = "0.4.0"
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
colored = "2.0"
|
||||
cupid = "0.6.1"
|
||||
dirs = "4.0"
|
||||
@@ -28,22 +29,24 @@ lazy_static = "1.4.0"
|
||||
log = { workspace = true }
|
||||
pretty_env_logger = "0.4.0"
|
||||
rand = "0.7.3"
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sysinfo = "0.27.7"
|
||||
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio-util = { version = "0.7.3", features = ["codec"] }
|
||||
toml = "0.5.8"
|
||||
url = { version = "2.2", features = ["serde"] }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
cfg-if = "1.0.0"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
## tracing
|
||||
tracing = { version = "0.1.37", optional = true }
|
||||
opentelemetry = { version = "0.19.0", optional = true }
|
||||
|
||||
|
||||
## internal
|
||||
# internal
|
||||
nym-node = { path = "../nym-node" }
|
||||
|
||||
nym-config = { path = "../common/config" }
|
||||
nym-crypto = { path = "../common/crypto" }
|
||||
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
|
||||
|
||||
@@ -62,7 +62,7 @@ fn init_paths(id: &str) -> io::Result<()> {
|
||||
fs::create_dir_all(default_config_directory(id))
|
||||
}
|
||||
|
||||
pub(crate) fn execute(args: &Init) {
|
||||
pub(crate) fn execute(args: &Init) -> anyhow::Result<()> {
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
let id = override_config_fields.id.clone();
|
||||
eprintln!("Initialising mixnode {id}...");
|
||||
@@ -118,5 +118,6 @@ pub(crate) fn execute(args: &Init) {
|
||||
);
|
||||
eprintln!("Mixnode configuration completed.\n\n\n");
|
||||
|
||||
MixNode::new(config).print_node_details(args.output)
|
||||
MixNode::new(config)?.print_node_details(args.output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+15
-27
@@ -1,12 +1,13 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::old_config_v1_1_21::ConfigV1_1_21;
|
||||
use crate::config::default_config_filepath;
|
||||
use crate::error::MixnodeError;
|
||||
use crate::{config::Config, Cli};
|
||||
use anyhow::anyhow;
|
||||
use clap::CommandFactory;
|
||||
use clap::Subcommand;
|
||||
use colored::Colorize;
|
||||
use log::{error, info, warn};
|
||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_bin_common::version_checker;
|
||||
use nym_config::defaults::var_names::{BECH32_PREFIX, NYM_API};
|
||||
@@ -21,6 +22,7 @@ mod init;
|
||||
mod node_details;
|
||||
mod run;
|
||||
mod sign;
|
||||
mod upgrade_helpers;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Commands {
|
||||
@@ -64,7 +66,7 @@ pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
|
||||
|
||||
match args.command {
|
||||
Commands::Describe(m) => describe::execute(m)?,
|
||||
Commands::Init(m) => init::execute(&m),
|
||||
Commands::Init(m) => init::execute(&m)?,
|
||||
Commands::Run(m) => run::execute(&m).await?,
|
||||
Commands::Sign(m) => sign::execute(&m)?,
|
||||
Commands::NodeDetails(m) => node_details::execute(&m)?,
|
||||
@@ -130,32 +132,18 @@ pub(crate) fn version_check(cfg: &Config) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_21_config(id: &str) -> std::io::Result<()> {
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
|
||||
// explicitly load it as v1.1.21 (which is incompatible with the current, i.e. 1.1.22+)
|
||||
let Ok(old_config) = ConfigV1_1_21::load_from_file(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(());
|
||||
};
|
||||
info!("It seems the mixnode is using <= v1.1.21 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
updated.save_to_default_location()
|
||||
}
|
||||
|
||||
fn try_load_current_config(id: &str) -> anyhow::Result<Config> {
|
||||
try_upgrade_v1_1_21_config(id)?;
|
||||
fn try_load_current_config(id: &str) -> Result<Config, MixnodeError> {
|
||||
upgrade_helpers::try_upgrade_config(id)?;
|
||||
|
||||
Config::read_from_default_path(id).map_err(|err| {
|
||||
let error_msg =
|
||||
format!(
|
||||
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
|
||||
);
|
||||
error!("{error_msg}");
|
||||
anyhow!(error_msg)
|
||||
error!(
|
||||
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
|
||||
);
|
||||
MixnodeError::ConfigLoadFailure {
|
||||
path: default_config_filepath(id),
|
||||
id: id.to_string(),
|
||||
source: err,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,6 @@ pub(crate) struct NodeDetails {
|
||||
pub(crate) fn execute(args: &NodeDetails) -> anyhow::Result<()> {
|
||||
let config = try_load_current_config(&args.id)?;
|
||||
|
||||
MixNode::new(config).print_node_details(args.output);
|
||||
MixNode::new(config)?.print_node_details(args.output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ use crate::commands::{override_config, try_load_current_config, version_check};
|
||||
use crate::node::MixNode;
|
||||
use anyhow::bail;
|
||||
use clap::Args;
|
||||
use log::error;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_config::helpers::SPECIAL_ADDRESSES;
|
||||
use nym_validator_client::nyxd;
|
||||
use std::net::IpAddr;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Run {
|
||||
/// Id of the nym-mixnode we want to run
|
||||
@@ -84,13 +84,13 @@ pub(crate) async fn execute(args: &Run) -> anyhow::Result<()> {
|
||||
show_binding_warning(&config.mixnode.listening_address.to_string());
|
||||
}
|
||||
|
||||
let mut mixnode = MixNode::new(config);
|
||||
let mut mixnode = MixNode::new(config)?;
|
||||
|
||||
eprintln!(
|
||||
"\nTo bond your mixnode you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\
|
||||
Select the correct version and install it to your machine. You will need to provide the following: \n ");
|
||||
mixnode.print_node_details(args.output);
|
||||
|
||||
mixnode.run().await;
|
||||
mixnode.run().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::{try_load_current_config, validate_bech32_address_or_exit};
|
||||
use crate::node::MixNode;
|
||||
use crate::node::helpers::load_identity_keys;
|
||||
use anyhow::{bail, Result};
|
||||
use clap::{ArgGroup, Args};
|
||||
use log::error;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_types::helpers::ConsoleSigningOutput;
|
||||
use nym_validator_client::nyxd;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[cfg(feature = "cpucycles")]
|
||||
use tracing::error;
|
||||
|
||||
use super::version_check;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
@@ -129,7 +127,7 @@ pub(crate) fn execute(args: &Sign) -> anyhow::Result<()> {
|
||||
bail!(err);
|
||||
}
|
||||
};
|
||||
let identity_keypair = MixNode::load_identity_keys(&config);
|
||||
let identity_keypair = load_identity_keys(&config)?;
|
||||
|
||||
match signed_target {
|
||||
SignedTarget::Text(text) => {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::old_config_v1_1_21::ConfigV1_1_21;
|
||||
use crate::config::old_config_v1_1_32::ConfigV1_1_32;
|
||||
use crate::config::{default_config_filepath, Config};
|
||||
use crate::error::MixnodeError;
|
||||
use log::info;
|
||||
|
||||
fn try_upgrade_v1_1_21_config(id: &str) -> Result<bool, MixnodeError> {
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
|
||||
// explicitly load it as v1.1.21 (which is incompatible with the current, i.e. 1.1.22+)
|
||||
let Ok(old_config) = ConfigV1_1_21::load_from_file(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the mixnode is using <= v1.1.21 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_32 = old_config.into();
|
||||
|
||||
let updated: Config = updated_step1.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| MixnodeError::ConfigSaveFailure {
|
||||
path: default_config_filepath(id),
|
||||
id: id.to_string(),
|
||||
source: err,
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_32_config(id: &str) -> Result<bool, MixnodeError> {
|
||||
// explicitly load it as v1.1.32 (which is incompatible with the current, i.e. 1.1.22+)
|
||||
let Ok(old_config) = ConfigV1_1_32::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the mixnode is using <= v1.1.32 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| MixnodeError::ConfigSaveFailure {
|
||||
path: default_config_filepath(id),
|
||||
id: id.to_string(),
|
||||
source: err,
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub(crate) fn try_upgrade_config(id: &str) -> Result<(), MixnodeError> {
|
||||
if try_upgrade_v1_1_21_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_32_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
+62
-10
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::config::persistence::paths::MixNodePaths;
|
||||
use crate::config::template::CONFIG_TEMPLATE;
|
||||
use log::{debug, warn};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::defaults::{
|
||||
mainnet, DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT,
|
||||
@@ -13,15 +14,17 @@ use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
|
||||
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
||||
};
|
||||
use nym_node::config;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::net::IpAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod old_config_v1_1_21;
|
||||
pub(crate) mod old_config_v1_1_32;
|
||||
pub mod persistence;
|
||||
mod template;
|
||||
|
||||
@@ -70,9 +73,28 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
.join(DEFAULT_DATA_DIR)
|
||||
}
|
||||
|
||||
fn default_mixnode_http_config() -> config::Http {
|
||||
config::Http {
|
||||
bind_address: SocketAddr::new(
|
||||
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
DEFAULT_HTTP_API_LISTENING_PORT,
|
||||
),
|
||||
landing_page_assets_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
// additional metadata holding on-disk location of this config file
|
||||
#[serde(skip)]
|
||||
pub(crate) save_path: Option<PathBuf>,
|
||||
|
||||
pub host: config::Host,
|
||||
|
||||
#[serde(default = "default_mixnode_http_config")]
|
||||
pub http: config::Http,
|
||||
|
||||
pub mixnode: MixNode,
|
||||
|
||||
pub storage_paths: MixNodePaths,
|
||||
@@ -95,8 +117,17 @@ impl NymConfigTemplate for Config {
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
let default_mixnode = MixNode::new_default(id.as_ref());
|
||||
|
||||
Config {
|
||||
mixnode: MixNode::new_default(id.as_ref()),
|
||||
save_path: None,
|
||||
host: config::Host {
|
||||
// this is a very bad default!
|
||||
public_ips: vec![default_mixnode.listening_address],
|
||||
hostname: None,
|
||||
},
|
||||
http: default_mixnode_http_config(),
|
||||
mixnode: default_mixnode,
|
||||
storage_paths: MixNodePaths::new_default(id.as_ref()),
|
||||
verloc: Default::default(),
|
||||
logging: Default::default(),
|
||||
@@ -104,12 +135,24 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
// simple wrapper that reads config file and assigns path location
|
||||
fn read_from_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
let path = path.as_ref();
|
||||
let mut loaded: Config = read_config_from_toml_file(path)?;
|
||||
loaded.save_path = Some(path.to_path_buf());
|
||||
debug!("loaded config file from {}", path.display());
|
||||
Ok(loaded)
|
||||
}
|
||||
|
||||
// currently this is dead code, but once we allow loading configs from custom paths
|
||||
// well, we will have to be using it
|
||||
#[allow(dead_code)]
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(path)
|
||||
Self::read_from_path(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_toml_file(default_config_filepath(id))
|
||||
Self::read_from_path(default_config_filepath(id))
|
||||
}
|
||||
|
||||
pub fn default_location(&self) -> PathBuf {
|
||||
@@ -121,6 +164,15 @@ impl Config {
|
||||
save_formatted_config_to_file(self, config_save_location)
|
||||
}
|
||||
|
||||
pub fn try_save(&self) -> io::Result<()> {
|
||||
if let Some(save_location) = &self.save_path {
|
||||
save_formatted_config_to_file(self, save_location)
|
||||
} else {
|
||||
warn!("config file save location is unknown. falling back to the default");
|
||||
self.save_to_default_location()
|
||||
}
|
||||
}
|
||||
|
||||
// builder methods
|
||||
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
|
||||
self.mixnode.nym_api_urls = nym_api_urls;
|
||||
@@ -129,6 +181,10 @@ impl Config {
|
||||
|
||||
pub fn with_listening_address(mut self, listening_address: IpAddr) -> Self {
|
||||
self.mixnode.listening_address = listening_address;
|
||||
|
||||
let http_port = self.http.bind_address.port();
|
||||
self.http.bind_address = SocketAddr::new(listening_address, http_port);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@@ -143,7 +199,8 @@ impl Config {
|
||||
}
|
||||
|
||||
pub fn with_http_api_port(mut self, port: u16) -> Self {
|
||||
self.mixnode.http_api_port = port;
|
||||
let http_ip = self.http.bind_address.ip();
|
||||
self.http.bind_address = SocketAddr::new(http_ip, port);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -171,10 +228,6 @@ pub struct MixNode {
|
||||
/// (default: 1790)
|
||||
pub verloc_port: u16,
|
||||
|
||||
/// Port used for listening for http requests.
|
||||
/// (default: 8000)
|
||||
pub http_api_port: u16,
|
||||
|
||||
/// Addresses to nym APIs from which the node gets the view of the network.
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
}
|
||||
@@ -187,7 +240,6 @@ impl MixNode {
|
||||
listening_address: inaddr_any(),
|
||||
mix_port: DEFAULT_MIX_LISTENING_PORT,
|
||||
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
|
||||
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
|
||||
nym_api_urls: vec![Url::from_str(mainnet::NYM_API).expect("Invalid default API URL")],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::old_config_v1_1_32::{
|
||||
ConfigV1_1_32, DebugV1_1_32, MixNodeV1_1_32, VerlocV1_1_32,
|
||||
};
|
||||
use crate::config::persistence::paths::{KeysPaths, MixNodePaths};
|
||||
use crate::config::{Config, Debug, MixNode, Verloc};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
use nym_validator_client::nyxd;
|
||||
@@ -67,13 +69,13 @@ pub struct ConfigV1_1_21 {
|
||||
debug: DebugV1_1_21,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_21> for Config {
|
||||
impl From<ConfigV1_1_21> for ConfigV1_1_32 {
|
||||
fn from(value: ConfigV1_1_21) -> Self {
|
||||
let node_description =
|
||||
ConfigV1_1_21::default_config_directory(&value.mixnode.id).join(DESCRIPTION_FILE);
|
||||
|
||||
Config {
|
||||
mixnode: MixNode {
|
||||
ConfigV1_1_32 {
|
||||
mixnode: MixNodeV1_1_32 {
|
||||
version: value.mixnode.version,
|
||||
id: value.mixnode.id,
|
||||
listening_address: value.mixnode.listening_address,
|
||||
@@ -169,9 +171,9 @@ struct VerlocV1_1_21 {
|
||||
retry_timeout: Duration,
|
||||
}
|
||||
|
||||
impl From<VerlocV1_1_21> for Verloc {
|
||||
impl From<VerlocV1_1_21> for VerlocV1_1_32 {
|
||||
fn from(value: VerlocV1_1_21) -> Self {
|
||||
Verloc {
|
||||
VerlocV1_1_32 {
|
||||
packets_per_node: value.packets_per_node,
|
||||
connection_timeout: value.connection_timeout,
|
||||
packet_timeout: value.packet_timeout,
|
||||
@@ -220,9 +222,9 @@ struct DebugV1_1_21 {
|
||||
use_legacy_framed_packet_version: bool,
|
||||
}
|
||||
|
||||
impl From<DebugV1_1_21> for Debug {
|
||||
impl From<DebugV1_1_21> for DebugV1_1_32 {
|
||||
fn from(value: DebugV1_1_21) -> Self {
|
||||
Debug {
|
||||
DebugV1_1_32 {
|
||||
node_stats_logging_delay: value.node_stats_logging_delay,
|
||||
node_stats_updating_delay: value.node_stats_updating_delay,
|
||||
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::MixNodePaths;
|
||||
use crate::config::{Config, Debug, MixNode, Verloc};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
const DEFAULT_MIXNODES_DIR: &str = "mixnodes";
|
||||
|
||||
// 'RTT MEASUREMENT'
|
||||
const DEFAULT_PACKETS_PER_NODE: usize = 100;
|
||||
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
|
||||
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
|
||||
const DEFAULT_BATCH_SIZE: usize = 50;
|
||||
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
|
||||
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
|
||||
|
||||
// 'DEBUG'
|
||||
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
|
||||
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
|
||||
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
|
||||
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
|
||||
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
|
||||
|
||||
/// Derive default path to mixnodes's config directory.
|
||||
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config`
|
||||
fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
must_get_home()
|
||||
.join(NYM_DIR)
|
||||
.join(DEFAULT_MIXNODES_DIR)
|
||||
.join(id)
|
||||
.join(DEFAULT_CONFIG_DIR)
|
||||
}
|
||||
|
||||
/// Derive default path to mixnodes's config file.
|
||||
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config/config.toml`
|
||||
fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_32 {
|
||||
pub mixnode: MixNodeV1_1_32,
|
||||
|
||||
// i hope this laziness is not going to backfire...
|
||||
pub storage_paths: MixNodePaths,
|
||||
|
||||
#[serde(default)]
|
||||
pub verloc: VerlocV1_1_32,
|
||||
|
||||
#[serde(default)]
|
||||
pub logging: LoggingSettings,
|
||||
|
||||
#[serde(default)]
|
||||
pub debug: DebugV1_1_32,
|
||||
}
|
||||
|
||||
impl ConfigV1_1_32 {
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_32> for Config {
|
||||
fn from(value: ConfigV1_1_32) -> Self {
|
||||
Config {
|
||||
// \/ ADDED
|
||||
save_path: None,
|
||||
// /\ ADDED
|
||||
|
||||
// \/ ADDED
|
||||
host: nym_node::config::Host {
|
||||
// this is a very bad default!
|
||||
public_ips: vec![value.mixnode.listening_address],
|
||||
hostname: None,
|
||||
},
|
||||
// /\ ADDED
|
||||
|
||||
// \/ ADDED
|
||||
http: nym_node::config::Http {
|
||||
bind_address: SocketAddr::new(
|
||||
value.mixnode.listening_address,
|
||||
value.mixnode.http_api_port,
|
||||
),
|
||||
landing_page_assets_path: None,
|
||||
},
|
||||
// /\ ADDED
|
||||
mixnode: MixNode {
|
||||
version: value.mixnode.version,
|
||||
id: value.mixnode.id,
|
||||
listening_address: value.mixnode.listening_address,
|
||||
mix_port: value.mixnode.mix_port,
|
||||
verloc_port: value.mixnode.verloc_port,
|
||||
nym_api_urls: value.mixnode.nym_api_urls,
|
||||
},
|
||||
storage_paths: value.storage_paths,
|
||||
verloc: value.verloc.into(),
|
||||
logging: value.logging,
|
||||
debug: value.debug.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct MixNodeV1_1_32 {
|
||||
/// Version of the mixnode for which this configuration was created.
|
||||
pub version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular mixnode.
|
||||
pub id: String,
|
||||
|
||||
/// Address to which this mixnode will bind to and will be listening for packets.
|
||||
pub listening_address: IpAddr,
|
||||
|
||||
/// Port used for listening for all mixnet traffic.
|
||||
/// (default: 1789)
|
||||
pub mix_port: u16,
|
||||
|
||||
/// Port used for listening for verloc traffic.
|
||||
/// (default: 1790)
|
||||
pub verloc_port: u16,
|
||||
|
||||
/// Port used for listening for http requests.
|
||||
/// (default: 8000)
|
||||
pub http_api_port: u16,
|
||||
|
||||
/// Addresses to nym APIs from which the node gets the view of the network.
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct VerlocV1_1_32 {
|
||||
/// Specifies number of echo packets sent to each node during a measurement run.
|
||||
pub packets_per_node: usize,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the connection to get established.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub connection_timeout: Duration,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_timeout: Duration,
|
||||
|
||||
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub delay_between_packets: Duration,
|
||||
|
||||
/// Specifies number of nodes being tested at once.
|
||||
pub tested_nodes_batch_size: usize,
|
||||
|
||||
/// Specifies delay between subsequent test runs.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub testing_interval: Duration,
|
||||
|
||||
/// Specifies delay between attempting to run the measurement again if the previous run failed
|
||||
/// due to being unable to get the list of nodes.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub retry_timeout: Duration,
|
||||
}
|
||||
|
||||
impl From<VerlocV1_1_32> for Verloc {
|
||||
fn from(value: VerlocV1_1_32) -> Self {
|
||||
Verloc {
|
||||
packets_per_node: value.packets_per_node,
|
||||
connection_timeout: value.connection_timeout,
|
||||
packet_timeout: value.packet_timeout,
|
||||
delay_between_packets: value.delay_between_packets,
|
||||
tested_nodes_batch_size: value.tested_nodes_batch_size,
|
||||
testing_interval: value.testing_interval,
|
||||
retry_timeout: value.retry_timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VerlocV1_1_32 {
|
||||
fn default() -> Self {
|
||||
VerlocV1_1_32 {
|
||||
packets_per_node: DEFAULT_PACKETS_PER_NODE,
|
||||
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
|
||||
packet_timeout: DEFAULT_PACKET_TIMEOUT,
|
||||
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
|
||||
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
|
||||
testing_interval: DEFAULT_TESTING_INTERVAL,
|
||||
retry_timeout: DEFAULT_RETRY_TIMEOUT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DebugV1_1_32 {
|
||||
/// Delay between each subsequent node statistics being logged to the console
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub node_stats_logging_delay: Duration,
|
||||
|
||||
/// Delay between each subsequent node statistics being updated
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub node_stats_updating_delay: Duration,
|
||||
|
||||
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_forwarding_initial_backoff: Duration,
|
||||
|
||||
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_forwarding_maximum_backoff: Duration,
|
||||
|
||||
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub initial_connection_timeout: Duration,
|
||||
|
||||
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
|
||||
pub maximum_connection_buffer_size: usize,
|
||||
|
||||
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
|
||||
// it's set to true by default. The reason for that decision is to preserve compatibility with the
|
||||
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
|
||||
// It shall be disabled in the subsequent releases.
|
||||
pub use_legacy_framed_packet_version: bool,
|
||||
}
|
||||
|
||||
impl From<DebugV1_1_32> for Debug {
|
||||
fn from(value: DebugV1_1_32) -> Self {
|
||||
Debug {
|
||||
node_stats_logging_delay: value.node_stats_logging_delay,
|
||||
node_stats_updating_delay: value.node_stats_updating_delay,
|
||||
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.initial_connection_timeout,
|
||||
maximum_connection_buffer_size: value.maximum_connection_buffer_size,
|
||||
use_legacy_framed_packet_version: value.use_legacy_framed_packet_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DebugV1_1_32 {
|
||||
fn default() -> Self {
|
||||
DebugV1_1_32 {
|
||||
node_stats_logging_delay: DEFAULT_NODE_STATS_LOGGING_DELAY,
|
||||
node_stats_updating_delay: DEFAULT_NODE_STATS_UPDATING_DELAY,
|
||||
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
|
||||
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
|
||||
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
|
||||
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
|
||||
use_legacy_framed_packet_version: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,19 @@ pub(crate) const CONFIG_TEMPLATE: &str = r#"
|
||||
|
||||
##### main base mixnode config options #####
|
||||
|
||||
[host]
|
||||
# Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections.
|
||||
# currently not in active use for mixnodes
|
||||
public_ips = [
|
||||
{{#each host.public_ips }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
# (temporary) Optional hostname of this node, for example nymtech.net.
|
||||
# currently not in active use for mixnodes
|
||||
hostname = '{{ host.hostname }}'
|
||||
|
||||
[mixnode]
|
||||
# Version of the mixnode for which this configuration was created.
|
||||
version = '{{ mixnode.version }}'
|
||||
@@ -29,10 +42,6 @@ mix_port = {{ mixnode.mix_port }}
|
||||
# (default: 1790)
|
||||
verloc_port = {{ mixnode.verloc_port }}
|
||||
|
||||
# Port used for listening for http requests.
|
||||
# (default: 8000)
|
||||
http_api_port = {{ mixnode.http_api_port }}
|
||||
|
||||
# Addresses to APIs running on validator from which the node gets the view of the network.
|
||||
nym_api_urls = [
|
||||
{{#each mixnode.nym_api_urls }}
|
||||
@@ -40,6 +49,14 @@ nym_api_urls = [
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
[http]
|
||||
# Socket address this node will use for binding its http API.
|
||||
# default: `0.0.0.0:8000`
|
||||
bind_address = '{{ http.bind_address }}'
|
||||
|
||||
# Path to assets directory of custom landing page of this node
|
||||
landing_page_assets_path = '{{ http.landing_page_assets_path }}'
|
||||
|
||||
[storage_paths]
|
||||
|
||||
# Path to file containing private identity key.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MixnodeError {
|
||||
#[error("failed to load {keys} keys from '{}' (private key) and '{}' (public key): {err}", .paths.private_key_path.display(), .paths.public_key_path.display())]
|
||||
KeyPairLoadFailure {
|
||||
keys: String,
|
||||
paths: nym_pemstore::KeyPairPath,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[error("failed to load {key} public key from '{}': {err}", .path.display())]
|
||||
PublicKeyLoadFailure {
|
||||
key: String,
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"failed to load config file for id {id} using path '{}'. detailed message: {source}", path.display()
|
||||
)]
|
||||
ConfigLoadFailure {
|
||||
id: String,
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"failed to save config file for id {id} using path '{}'. detailed message: {source}", path.display()
|
||||
)]
|
||||
ConfigSaveFailure {
|
||||
id: String,
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
// TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors
|
||||
#[error(transparent)]
|
||||
NymNodeError(#[from] nym_node::error::NymNodeError),
|
||||
}
|
||||
+2
-3
@@ -1,12 +1,10 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
use ::nym_config::defaults::setup_env;
|
||||
use clap::{crate_name, crate_version, Parser};
|
||||
use lazy_static::lazy_static;
|
||||
use log::info;
|
||||
use nym_bin_common::bin_info;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
@@ -20,6 +18,7 @@ use tracing::instrument;
|
||||
|
||||
mod commands;
|
||||
mod config;
|
||||
pub(crate) mod error;
|
||||
mod node;
|
||||
|
||||
lazy_static! {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::MixnodeError;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use nym_pemstore::KeyPairPath;
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn load_keypair<T: PemStorableKeyPair>(
|
||||
paths: KeyPairPath,
|
||||
name: impl Into<String>,
|
||||
) -> Result<T, MixnodeError> {
|
||||
nym_pemstore::load_keypair(&paths).map_err(|err| MixnodeError::KeyPairLoadFailure {
|
||||
keys: name.into(),
|
||||
paths,
|
||||
err,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn load_public_key<T, P, S>(path: P, name: S) -> Result<T, MixnodeError>
|
||||
where
|
||||
T: PemStorableKey,
|
||||
P: AsRef<Path>,
|
||||
S: Into<String>,
|
||||
{
|
||||
nym_pemstore::load_key(path.as_ref()).map_err(|err| MixnodeError::PublicKeyLoadFailure {
|
||||
key: name.into(),
|
||||
path: path.as_ref().to_path_buf(),
|
||||
err,
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads identity keys stored on disk
|
||||
pub(crate) fn load_identity_keys(config: &Config) -> Result<identity::KeyPair, MixnodeError> {
|
||||
let identity_paths = KeyPairPath::new(
|
||||
config.storage_paths.keys.private_identity_key(),
|
||||
config.storage_paths.keys.public_identity_key(),
|
||||
);
|
||||
load_keypair(identity_paths, "mixnode identity")
|
||||
}
|
||||
|
||||
/// Loads Sphinx keys stored on disk
|
||||
pub(crate) fn load_sphinx_keys(config: &Config) -> Result<encryption::KeyPair, MixnodeError> {
|
||||
let sphinx_paths = KeyPairPath::new(
|
||||
config.storage_paths.keys.private_encryption_key(),
|
||||
config.storage_paths.keys.public_encryption_key(),
|
||||
);
|
||||
load_keypair(sphinx_paths, "mixnode sphinx")
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
|
||||
/// Returns a description of the node and why someone might want to delegate stake to it.
|
||||
#[get("/description")]
|
||||
pub(crate) fn description(description: &State<NodeDescription>) -> Json<NodeDescription> {
|
||||
Json(description.inner().clone())
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use axum::extract::Query;
|
||||
use nym_node::http::api::{FormattedResponse, OutputParams};
|
||||
|
||||
/// Returns a description of the node and why someone might want to delegate stake to it.
|
||||
pub(crate) async fn description(
|
||||
description: NodeDescription,
|
||||
Query(output): Query<OutputParams>,
|
||||
) -> MixnodeDescriptionResponse {
|
||||
let output = output.output.unwrap_or_default();
|
||||
output.to_response(description)
|
||||
}
|
||||
|
||||
pub type MixnodeDescriptionResponse = FormattedResponse<NodeDescription>;
|
||||
@@ -1,10 +1,11 @@
|
||||
use axum::extract::Query;
|
||||
use cupid::TopologyType;
|
||||
use rocket::serde::{json::Json, Serialize};
|
||||
use nym_node::http::api::{FormattedResponse, OutputParams};
|
||||
use serde::Serialize;
|
||||
use sysinfo::{System, SystemExt};
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub(crate) struct Hardware {
|
||||
pub struct Hardware {
|
||||
ram: String,
|
||||
num_cores: usize,
|
||||
crypto_hardware: Option<CryptoHardware>,
|
||||
@@ -12,7 +13,6 @@ pub(crate) struct Hardware {
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Serialize, Debug)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub(crate) struct CryptoHardware {
|
||||
aesni: bool,
|
||||
avx2: bool,
|
||||
@@ -24,11 +24,13 @@ pub(crate) struct CryptoHardware {
|
||||
}
|
||||
|
||||
/// Provides hardware information which Nym can use to optimize mixnet speed over time (memory, crypto hardware, CPU, cores, etc).
|
||||
#[get("/hardware")]
|
||||
pub(crate) fn hardware() -> Json<Option<Hardware>> {
|
||||
Json(hardware_info())
|
||||
pub(crate) async fn hardware(Query(output): Query<OutputParams>) -> MixnodeHardwareResponse {
|
||||
let output = output.output.unwrap_or_default();
|
||||
output.to_response(hardware_info())
|
||||
}
|
||||
|
||||
pub type MixnodeHardwareResponse = FormattedResponse<Option<Hardware>>;
|
||||
|
||||
/// Gives back a summary report of whatever system hardware info we can get for this platform.
|
||||
fn hardware_info() -> Option<Hardware> {
|
||||
let crypto_hardware = hardware_info_from_cupid();
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node::http::legacy::description::description;
|
||||
use crate::node::http::legacy::hardware::hardware;
|
||||
use crate::node::http::legacy::state::MixnodeAppState;
|
||||
use crate::node::http::legacy::stats::stats;
|
||||
use crate::node::http::legacy::verloc::verloc;
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use axum::http::{StatusCode, Uri};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
|
||||
pub(crate) mod description;
|
||||
pub(crate) mod hardware;
|
||||
pub(crate) mod state;
|
||||
pub(crate) mod stats;
|
||||
pub(crate) mod verloc;
|
||||
|
||||
pub(crate) async fn not_found(uri: Uri) -> impl IntoResponse {
|
||||
(
|
||||
StatusCode::NOT_FOUND,
|
||||
format!("I couldn't find '{uri}'. Try something else?"),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) mod api_routes {
|
||||
pub(crate) const VERLOC: &str = "/verloc";
|
||||
pub(crate) const DESCRIPTION: &str = "/description";
|
||||
pub(crate) const STATS: &str = "/stats";
|
||||
pub(crate) const HARDWARE: &str = "/hardware";
|
||||
}
|
||||
|
||||
pub(crate) fn routes<S: Send + Sync + 'static + Clone>(
|
||||
state: MixnodeAppState,
|
||||
descriptor: NodeDescription,
|
||||
) -> Router<S> {
|
||||
Router::new()
|
||||
.route(api_routes::VERLOC, get(verloc))
|
||||
.route(
|
||||
api_routes::DESCRIPTION,
|
||||
get(move |query| description(descriptor, query)),
|
||||
)
|
||||
.route(api_routes::STATS, get(stats))
|
||||
.route(api_routes::HARDWARE, get(hardware))
|
||||
.fallback(not_found)
|
||||
.with_state(state)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node::http::legacy::verloc::VerlocState;
|
||||
use crate::node::node_statistics::SharedNodeStats;
|
||||
use axum::extract::FromRef;
|
||||
|
||||
// this is a temporary thing for the transition period
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct MixnodeAppState {
|
||||
pub(crate) verloc: VerlocState,
|
||||
pub(crate) stats: SharedNodeStats,
|
||||
}
|
||||
|
||||
impl FromRef<MixnodeAppState> for VerlocState {
|
||||
fn from_ref(app_state: &MixnodeAppState) -> Self {
|
||||
app_state.verloc.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<MixnodeAppState> for SharedNodeStats {
|
||||
fn from_ref(app_state: &MixnodeAppState) -> Self {
|
||||
app_state.stats.clone()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use crate::node::node_statistics::{NodeStats, NodeStatsSimple, SharedNodeStats};
|
||||
use axum::extract::{Query, State};
|
||||
use nym_node::http::api::{FormattedResponse, Output};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum NodeStatsResponse {
|
||||
Full(NodeStats),
|
||||
Simple(NodeStatsSimple),
|
||||
}
|
||||
|
||||
pub(crate) async fn stats(
|
||||
Query(params): Query<StatsQueryParams>,
|
||||
State(stats): State<SharedNodeStats>,
|
||||
) -> MixnodeStatsResponse {
|
||||
let output = params.output.unwrap_or_default();
|
||||
|
||||
let snapshot_data = stats.clone_data().await;
|
||||
|
||||
// there's no point in returning the entire hashmap of sending destinations in regular mode
|
||||
let response = if params.debug {
|
||||
NodeStatsResponse::Full(snapshot_data)
|
||||
} else {
|
||||
NodeStatsResponse::Simple(snapshot_data.simplify())
|
||||
};
|
||||
output.to_response(response)
|
||||
}
|
||||
|
||||
pub type MixnodeStatsResponse = FormattedResponse<NodeStatsResponse>;
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
// #[derive(Default, Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema)]
|
||||
#[serde(default)]
|
||||
pub(crate) struct StatsQueryParams {
|
||||
debug: bool,
|
||||
pub output: Option<Output>,
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
use axum::extract::{Query, State};
|
||||
use nym_mixnode_common::verloc::{AtomicVerlocResult, VerlocResult};
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use nym_node::http::api::{FormattedResponse, OutputParams};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct VerlocState {
|
||||
shared: AtomicVerlocResult,
|
||||
}
|
||||
@@ -16,8 +17,12 @@ impl VerlocState {
|
||||
|
||||
/// Provides verifiable location (verloc) measurements for this mixnode - a list of the
|
||||
/// round-trip times, in milliseconds, for all other mixnodes that this node knows about.
|
||||
#[get("/verloc")]
|
||||
pub(crate) async fn verloc(state: &State<VerlocState>) -> Json<VerlocResult> {
|
||||
// since it's impossible to get a mutable reference to the state, we can't cache any results outside the lock : (
|
||||
Json(state.shared.clone_data().await)
|
||||
pub(crate) async fn verloc(
|
||||
State(verloc): State<VerlocState>,
|
||||
Query(output): Query<OutputParams>,
|
||||
) -> MixnodeVerlocResponse {
|
||||
let output = output.output.unwrap_or_default();
|
||||
output.to_response(verloc.shared.clone_data().await)
|
||||
}
|
||||
|
||||
pub type MixnodeVerlocResponse = FormattedResponse<VerlocResult>;
|
||||
@@ -1,11 +1,105 @@
|
||||
pub(crate) mod description;
|
||||
pub(crate) mod hardware;
|
||||
pub(crate) mod stats;
|
||||
pub(crate) mod verloc;
|
||||
use crate::config::Config;
|
||||
use crate::error::MixnodeError;
|
||||
use crate::node::http::legacy::verloc::VerlocState;
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use crate::node::node_statistics::SharedNodeStats;
|
||||
use log::info;
|
||||
use nym_bin_common::bin_info_owned;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_node::error::NymNodeError;
|
||||
use nym_node::http::api::api_requests;
|
||||
use nym_node::http::api::api_requests::SignedHostInformation;
|
||||
use nym_task::TaskClient;
|
||||
|
||||
use rocket::Request;
|
||||
pub(crate) mod legacy;
|
||||
|
||||
#[catch(404)]
|
||||
pub(crate) fn not_found(req: &Request<'_>) -> String {
|
||||
format!("I couldn't find '{}'. Try something else?", req.uri())
|
||||
fn load_host_details(
|
||||
config: &Config,
|
||||
sphinx_key: &encryption::PublicKey,
|
||||
identity_keypair: &identity::KeyPair,
|
||||
) -> Result<api_requests::v1::node::models::SignedHostInformation, MixnodeError> {
|
||||
let host_info = api_requests::v1::node::models::HostInformation {
|
||||
ip_address: config.host.public_ips.clone(),
|
||||
hostname: config.host.hostname.clone(),
|
||||
keys: api_requests::v1::node::models::HostKeys {
|
||||
ed25519: identity_keypair.public_key().to_base58_string(),
|
||||
x25519: sphinx_key.to_base58_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let signed_info = SignedHostInformation::new(host_info, identity_keypair.private_key())
|
||||
.map_err(NymNodeError::from)?;
|
||||
Ok(signed_info)
|
||||
}
|
||||
|
||||
fn load_mixnode_details(
|
||||
_config: &Config,
|
||||
) -> Result<api_requests::v1::mixnode::models::Mixnode, MixnodeError> {
|
||||
Ok(api_requests::v1::mixnode::models::Mixnode {})
|
||||
}
|
||||
|
||||
pub(crate) struct HttpApiBuilder<'a> {
|
||||
mixnode_config: &'a Config,
|
||||
identity_keypair: &'a identity::KeyPair,
|
||||
sphinx_keypair: &'a encryption::KeyPair,
|
||||
legacy_mixnode: legacy::state::MixnodeAppState,
|
||||
legacy_descriptor: NodeDescription,
|
||||
}
|
||||
|
||||
impl<'a> HttpApiBuilder<'a> {
|
||||
pub(crate) fn new(
|
||||
mixnode_config: &'a Config,
|
||||
identity_keypair: &'a identity::KeyPair,
|
||||
sphinx_keypair: &'a encryption::KeyPair,
|
||||
) -> Self {
|
||||
HttpApiBuilder {
|
||||
mixnode_config,
|
||||
identity_keypair,
|
||||
sphinx_keypair,
|
||||
legacy_mixnode: legacy::state::MixnodeAppState::default(),
|
||||
legacy_descriptor: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_verloc(mut self, verloc: VerlocState) -> Self {
|
||||
self.legacy_mixnode.verloc = verloc;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_mixing_stats(mut self, stats: SharedNodeStats) -> Self {
|
||||
self.legacy_mixnode.stats = stats;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_descriptor(mut self, descriptor: NodeDescription) -> Self {
|
||||
self.legacy_descriptor = descriptor;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn start(self, task_client: TaskClient) -> Result<(), MixnodeError> {
|
||||
let bind_address = self.mixnode_config.http.bind_address;
|
||||
info!("Starting HTTP API on http://{bind_address}",);
|
||||
|
||||
let config = nym_node::http::Config::new(
|
||||
bin_info_owned!(),
|
||||
load_host_details(
|
||||
self.mixnode_config,
|
||||
self.sphinx_keypair.public_key(),
|
||||
self.identity_keypair,
|
||||
)?,
|
||||
)
|
||||
.with_mixnode(load_mixnode_details(self.mixnode_config)?)
|
||||
.with_landing_page_assets(self.mixnode_config.http.landing_page_assets_path.as_ref());
|
||||
|
||||
let router = nym_node::http::NymNodeRouter::new(config, None);
|
||||
let server = router
|
||||
.with_merged(legacy::routes(self.legacy_mixnode, self.legacy_descriptor))
|
||||
.build_server(&bind_address)?
|
||||
.with_task_client(task_client);
|
||||
tokio::spawn(async move { server.run().await });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
use crate::node::node_statistics::{NodeStats, NodeStatsSimple, SharedNodeStats};
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub(crate) enum NodeStatsResponse {
|
||||
Full(NodeStats),
|
||||
Simple(NodeStatsSimple),
|
||||
}
|
||||
|
||||
/// Returns a running stats of the node.
|
||||
#[get("/stats?<debug>")]
|
||||
pub(crate) async fn stats(
|
||||
stats: &State<SharedNodeStats>,
|
||||
debug: Option<bool>,
|
||||
) -> Json<NodeStatsResponse> {
|
||||
let snapshot_data = stats.clone_data().await;
|
||||
|
||||
// there's no point in returning the entire hashmap of sending destinations in regular mode
|
||||
if let Some(debug) = debug {
|
||||
if debug {
|
||||
return Json(NodeStatsResponse::Full(snapshot_data));
|
||||
}
|
||||
}
|
||||
|
||||
Json(NodeStatsResponse::Simple(snapshot_data.simplify()))
|
||||
}
|
||||
@@ -7,6 +7,8 @@ use crate::node::listener::connection_handler::packet_processing::{
|
||||
use crate::node::packet_delayforwarder::PacketDelayForwardSender;
|
||||
use crate::node::TaskClient;
|
||||
use futures::StreamExt;
|
||||
use log::debug;
|
||||
use log::{error, info, warn};
|
||||
use nym_mixnode_common::measure;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_sphinx::framing::codec::NymCodec;
|
||||
@@ -16,8 +18,9 @@ use std::net::SocketAddr;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::Instant;
|
||||
use tokio_util::codec::Framed;
|
||||
|
||||
#[cfg(feature = "cpucycles")]
|
||||
use tracing::{error, info, instrument};
|
||||
use tracing::instrument;
|
||||
|
||||
pub(crate) mod packet_processing;
|
||||
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node::listener::connection_handler::ConnectionHandler;
|
||||
use log::{error, info, warn};
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::task::JoinHandle;
|
||||
#[cfg(feature = "cpucycles")]
|
||||
use tracing::error;
|
||||
|
||||
use super::TaskClient;
|
||||
|
||||
|
||||
+27
-67
@@ -2,19 +2,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::node::http::{
|
||||
description::description,
|
||||
hardware::hardware,
|
||||
not_found,
|
||||
stats::stats,
|
||||
verloc::{verloc as verloc_route, VerlocState},
|
||||
};
|
||||
use crate::error::MixnodeError;
|
||||
use crate::node::helpers::{load_identity_keys, load_sphinx_keys};
|
||||
use crate::node::http::legacy::verloc::VerlocState;
|
||||
use crate::node::http::HttpApiBuilder;
|
||||
use crate::node::listener::connection_handler::packet_processing::PacketProcessor;
|
||||
use crate::node::listener::connection_handler::ConnectionHandler;
|
||||
use crate::node::listener::Listener;
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use crate::node::node_statistics::SharedNodeStats;
|
||||
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
|
||||
use log::{error, info, warn};
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_bin_common::version_checker::parse_version;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
@@ -26,9 +24,7 @@ use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "cpucycles")]
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
pub(crate) mod helpers;
|
||||
mod http;
|
||||
mod listener;
|
||||
pub(crate) mod node_description;
|
||||
@@ -44,41 +40,19 @@ pub struct MixNode {
|
||||
}
|
||||
|
||||
impl MixNode {
|
||||
pub fn new(config: Config) -> Self {
|
||||
MixNode {
|
||||
pub fn new(config: Config) -> Result<Self, MixnodeError> {
|
||||
Ok(MixNode {
|
||||
descriptor: Self::load_node_description(&config),
|
||||
identity_keypair: Arc::new(Self::load_identity_keys(&config)),
|
||||
sphinx_keypair: Arc::new(Self::load_sphinx_keys(&config)),
|
||||
identity_keypair: Arc::new(load_identity_keys(&config)?),
|
||||
sphinx_keypair: Arc::new(load_sphinx_keys(&config)?),
|
||||
config,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn load_node_description(config: &Config) -> NodeDescription {
|
||||
NodeDescription::load_from_file(&config.storage_paths.node_description).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Loads identity keys stored on disk
|
||||
pub(crate) fn load_identity_keys(config: &Config) -> identity::KeyPair {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||
config.storage_paths.keys.private_identity_key(),
|
||||
config.storage_paths.keys.public_identity_key(),
|
||||
))
|
||||
.expect("Failed to read stored identity key files");
|
||||
identity_keypair
|
||||
}
|
||||
|
||||
/// Loads Sphinx keys stored on disk
|
||||
fn load_sphinx_keys(config: &Config) -> encryption::KeyPair {
|
||||
let sphinx_keypair: encryption::KeyPair =
|
||||
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||
config.storage_paths.keys.private_encryption_key(),
|
||||
config.storage_paths.keys.public_encryption_key(),
|
||||
))
|
||||
.expect("Failed to read stored sphinx key files");
|
||||
sphinx_keypair
|
||||
}
|
||||
|
||||
/// Prints relevant node details to the console
|
||||
pub(crate) fn print_node_details(&self, output: OutputFormat) {
|
||||
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
|
||||
@@ -87,7 +61,7 @@ impl MixNode {
|
||||
bind_address: self.config.mixnode.listening_address,
|
||||
version: self.config.mixnode.version.clone(),
|
||||
mix_port: self.config.mixnode.mix_port,
|
||||
http_api_port: self.config.mixnode.http_api_port,
|
||||
http_api_port: self.config.http.bind_address.port(),
|
||||
verloc_port: self.config.mixnode.verloc_port,
|
||||
};
|
||||
|
||||
@@ -98,32 +72,13 @@ impl MixNode {
|
||||
&self,
|
||||
atomic_verloc_result: AtomicVerlocResult,
|
||||
node_stats_pointer: SharedNodeStats,
|
||||
) {
|
||||
info!(
|
||||
"Starting HTTP API on http://{}:{}",
|
||||
self.config.mixnode.listening_address, self.config.mixnode.http_api_port
|
||||
);
|
||||
|
||||
let mut config = rocket::config::Config::release_default();
|
||||
|
||||
// bind to the same address as we are using for mixnodes
|
||||
config.address = self.config.mixnode.listening_address;
|
||||
config.port = self.config.mixnode.http_api_port;
|
||||
|
||||
let verloc_state = VerlocState::new(atomic_verloc_result);
|
||||
let descriptor = self.descriptor.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
rocket::build()
|
||||
.configure(config)
|
||||
.mount("/", routes![verloc_route, description, stats, hardware])
|
||||
.register("/", catchers![not_found])
|
||||
.manage(verloc_state)
|
||||
.manage(descriptor)
|
||||
.manage(node_stats_pointer)
|
||||
.launch()
|
||||
.await
|
||||
});
|
||||
task_client: TaskClient,
|
||||
) -> Result<(), MixnodeError> {
|
||||
HttpApiBuilder::new(&self.config, &self.identity_keypair, &self.sphinx_keypair)
|
||||
.with_verloc(VerlocState::new(atomic_verloc_result))
|
||||
.with_mixing_stats(node_stats_pointer)
|
||||
.with_descriptor(self.descriptor.clone())
|
||||
.start(task_client)
|
||||
}
|
||||
|
||||
fn start_node_stats_controller(
|
||||
@@ -265,7 +220,7 @@ impl MixNode {
|
||||
log::info!("Stopping nym mixnode");
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
pub async fn run(&mut self) -> Result<(), MixnodeError> {
|
||||
info!("Starting nym mixnode");
|
||||
|
||||
if self.check_if_bonded().await {
|
||||
@@ -291,9 +246,14 @@ impl MixNode {
|
||||
// Rocket handles shutdown on it's own, but its shutdown handling should be incorporated
|
||||
// with that of the rest of the tasks.
|
||||
// Currently it's runtime is forcefully terminated once the mixnode exits.
|
||||
self.start_http_api(atomic_verloc_results, node_stats_pointer);
|
||||
self.start_http_api(
|
||||
atomic_verloc_results,
|
||||
node_stats_pointer,
|
||||
shutdown.subscribe().named("http-api"),
|
||||
)?;
|
||||
|
||||
info!("Finished nym mixnode startup procedure - it should now be able to receive mix traffic!");
|
||||
self.wait_for_interrupt(shutdown).await
|
||||
self.wait_for_interrupt(shutdown).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use super::TaskClient;
|
||||
use futures::channel::mpsc;
|
||||
use futures::lock::Mutex;
|
||||
use futures::StreamExt;
|
||||
use log::{debug, info, trace};
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::DerefMut;
|
||||
@@ -9,14 +11,12 @@ use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
|
||||
use super::TaskClient;
|
||||
|
||||
// convenience aliases
|
||||
type PacketsMap = HashMap<String, u64>;
|
||||
type PacketDataReceiver = mpsc::UnboundedReceiver<PacketEvent>;
|
||||
type PacketDataSender = mpsc::UnboundedSender<PacketEvent>;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct SharedNodeStats {
|
||||
inner: Arc<RwLock<NodeStats>>,
|
||||
}
|
||||
@@ -80,7 +80,7 @@ impl SharedNodeStats {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct NodeStats {
|
||||
pub struct NodeStats {
|
||||
#[serde(serialize_with = "humantime_serde::serialize")]
|
||||
update_time: SystemTime,
|
||||
|
||||
@@ -104,6 +104,21 @@ pub(crate) struct NodeStats {
|
||||
packets_explicitly_dropped_since_last_update: PacketsMap,
|
||||
}
|
||||
|
||||
impl Default for NodeStats {
|
||||
fn default() -> Self {
|
||||
NodeStats {
|
||||
update_time: SystemTime::UNIX_EPOCH,
|
||||
previous_update_time: SystemTime::UNIX_EPOCH,
|
||||
packets_received_since_startup: 0,
|
||||
packets_sent_since_startup: Default::default(),
|
||||
packets_explicitly_dropped_since_startup: Default::default(),
|
||||
packets_received_since_last_update: 0,
|
||||
packets_sent_since_last_update: Default::default(),
|
||||
packets_explicitly_dropped_since_last_update: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeStats {
|
||||
pub(crate) fn simplify(&self) -> NodeStatsSimple {
|
||||
NodeStatsSimple {
|
||||
@@ -126,7 +141,7 @@ impl NodeStats {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct NodeStatsSimple {
|
||||
pub struct NodeStatsSimple {
|
||||
#[serde(serialize_with = "humantime_serde::serialize")]
|
||||
update_time: SystemTime,
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ async-trait = { workspace = true }
|
||||
bs58 = { version = "0.4.0" }
|
||||
bip39 = { workspace = true }
|
||||
cfg-if = "1.0"
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
console-subscriber = { version = "0.1.1", optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable"
|
||||
dirs = "4.0"
|
||||
futures = { workspace = true }
|
||||
|
||||
Generated
+12
-14
@@ -126,16 +126,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
@@ -165,9 +164,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.2"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
@@ -868,20 +867,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.21"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
|
||||
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.21"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
|
||||
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -910,9 +908,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.3.12"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
@@ -922,9 +920,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
|
||||
@@ -43,6 +43,7 @@ impl Host {
|
||||
pub struct Http {
|
||||
/// Socket address this node will use for binding its http API.
|
||||
/// default: `0.0.0.0:8080`
|
||||
/// note: for legacy reasons, it defaults to port `8000` for mixnodes.
|
||||
pub bind_address: SocketAddr,
|
||||
|
||||
/// Path to assets directory of custom landing page of this node.
|
||||
|
||||
@@ -124,6 +124,12 @@ impl NymNodeRouter {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_merged(mut self, router: Router) -> Self {
|
||||
self.inner = self.inner.merge(router);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build_server(
|
||||
self,
|
||||
bind_address: &SocketAddr,
|
||||
|
||||
Generated
+23
-15
@@ -108,16 +108,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
@@ -147,9 +146,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.2"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
@@ -662,20 +661,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.21"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
|
||||
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.21"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
|
||||
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -704,9 +702,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.3.12"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
@@ -716,9 +714,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
@@ -3431,6 +3429,15 @@ dependencies = [
|
||||
"nym-contracts-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-exit-policy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-group-contract-common"
|
||||
version = "0.1.0"
|
||||
@@ -3510,6 +3517,7 @@ dependencies = [
|
||||
"base64 0.21.4",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"nym-exit-policy",
|
||||
"nym-wireguard-types",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -3698,7 +3706,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.9"
|
||||
version = "1.2.10"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
|
||||
+4
-2
@@ -10,6 +10,8 @@
|
||||
"sdk/typescript/packages/mui-theme",
|
||||
"sdk/typescript/packages/react-components",
|
||||
"sdk/typescript/packages/validator-client",
|
||||
"sdk/typescript/packages/mix-fetch-node",
|
||||
"sdk/typescript/packages/nodejs-client",
|
||||
"ts-packages/*",
|
||||
"nym-wallet",
|
||||
"nym-connect/**",
|
||||
@@ -34,7 +36,7 @@
|
||||
"prebuild:ci": "yarn dev:on && yarn",
|
||||
"build:ci": "run-s build:types build:packages build:wasm build:ci:sdk",
|
||||
"postbuild:ci": "yarn dev:off",
|
||||
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/sdk-react,@nymproject/mix-fetch}' build:dev --stream",
|
||||
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' build --stream",
|
||||
"docs:prod:build": "run-s docs:prod:build:ws",
|
||||
"docs:prod:build:ws": "lerna run docs:prod:build --stream",
|
||||
"sdk:build": "./sdk/typescript/scripts/build-prod-sdk.sh",
|
||||
@@ -53,4 +55,4 @@
|
||||
"@npmcli/node-gyp": "^3.0.0",
|
||||
"node-gyp": "^9.3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
const { createMixFetch, disconnectMixFetch } = require('@nymproject/mix-fetch-node-commonjs');
|
||||
|
||||
/**
|
||||
* The main entry point
|
||||
*/
|
||||
(async () => {
|
||||
console.log('Tester is starting up...');
|
||||
|
||||
const addr =
|
||||
'D274yd1h3L3pNJzdxE5VgJ7izAsAVMsDrQtFSkKUegfk.8J67cGbcwvrJKF3Kb16HVWWc9AnrFnEibNCm9zCkuVFu@Emswx6KXyjRfq1c2k4d4uD2e6nBSbH1biorCZUei8UNS';
|
||||
|
||||
console.log('About to set up mixFetch...');
|
||||
const { mixFetch } = await createMixFetch({
|
||||
preferredNetworkRequester: addr,
|
||||
clientId: 'node-client1',
|
||||
clientOverride: {
|
||||
coverTraffic: { disableLoopCoverTrafficStream: true },
|
||||
traffic: { disableMainPoissonPacketDistribution: true },
|
||||
},
|
||||
mixFetchOverride: { requestTimeoutMs: 60000 },
|
||||
responseBodyConfigMap: {},
|
||||
extra: {},
|
||||
});
|
||||
|
||||
globalThis.mixFetch = mixFetch;
|
||||
|
||||
if (!globalThis.mixFetch) {
|
||||
console.error('Oh no! Could not create mixFetch');
|
||||
} else {
|
||||
console.log('Ready!');
|
||||
}
|
||||
|
||||
let url = 'https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt';
|
||||
console.log(`Using mixFetch to get ${url}...`);
|
||||
const args = { mode: 'unsafe-ignore-cors' };
|
||||
|
||||
let resp = await mixFetch(url, args);
|
||||
console.log({ resp });
|
||||
const text = await resp.text();
|
||||
|
||||
console.log('disconnecting');
|
||||
await disconnectMixFetch();
|
||||
console.log('disconnected! all further usages should fail');
|
||||
|
||||
// get an image
|
||||
url = 'https://nymtech.net/favicon.svg';
|
||||
resp = await mixFetch(url, args);
|
||||
console.log({ resp });
|
||||
const buffer = await resp.arrayBuffer();
|
||||
const type = resp.headers.get('Content-Type') || 'image/svg';
|
||||
const blobUrl = URL.createObjectURL(new Blob([buffer], { type }));
|
||||
console.log(JSON.stringify({ bufferBytes: buffer.byteLength, blobUrl }, null, 2));
|
||||
console.log(blobUrl);
|
||||
})();
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@nymproject/mix-fetch-node-js-example",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"start:server": "node server.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/mix-fetch-node-commonjs": "^1.2.1-rc.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
const express = require('express');
|
||||
const { mixFetch } = require('@nymproject/mix-fetch-node-commonjs');
|
||||
|
||||
const app = express();
|
||||
app.use(express.static('public'));
|
||||
|
||||
app.get('/nym-fetch', async (req, res) => {
|
||||
try {
|
||||
const args = {
|
||||
mode: 'unsafe-ignore-cors',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const url = req.query.url;
|
||||
|
||||
if (!url) {
|
||||
return res.status(400).send('input a valid url');
|
||||
}
|
||||
|
||||
const extra = {
|
||||
hiddenGateways: [
|
||||
{
|
||||
owner: 'n1ns3v70ul9gnl9l9fkyz8cyxfq75vjcmx8el0t3',
|
||||
host: 'sandbox-gateway1.nymtech.net',
|
||||
explicitIp: '35.158.238.80',
|
||||
identityKey: 'HjNEDJuotWV8VD4ufeA1jeheTnfNJ7Jorevp57hgaZua',
|
||||
sphinxKey: 'BoXeUD7ERGmzRauMjJD3itVNnQiH42ncUb6kcVLrb3dy',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mixFetchOptions = {
|
||||
nymApiUrl: 'https://sandbox-nym-api1.nymtech.net/api',
|
||||
preferredGateway: 'HjNEDJuotWV8VD4ufeA1jeheTnfNJ7Jorevp57hgaZua',
|
||||
preferredNetworkRequester:
|
||||
'AzGdJ4MU78Ex22NEWfeycbN7bt3PFZr1MtKstAdhfELG.GSxnKnvKPjjQm3FdtsgG5KyhP6adGbPHRmFWDH4XfUpP@HjNEDJuotWV8VD4ufeA1jeheTnfNJ7Jorevp57hgaZua',
|
||||
mixFetchOverride: {
|
||||
requestTimeoutMs: 60_000,
|
||||
},
|
||||
forceTls: false,
|
||||
extra,
|
||||
};
|
||||
|
||||
const response = await mixFetch(url, args, mixFetchOptions);
|
||||
const json = await response.json();
|
||||
res.send(json);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(500).send(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000, () => console.log('Server running on port 3000'));
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user