Compare commits

..

2 Commits

Author SHA1 Message Date
serinko 0cc8cf0846 update plugins 2023-10-16 14:04:22 +02:00
serinko 87b3b76854 update mdbook admonish 2023-10-16 13:46:16 +02:00
547 changed files with 7184 additions and 49128 deletions
@@ -1,37 +0,0 @@
name: 'Install wasm-opt'
description: 'Installs wasm-opt from binaryen'
inputs:
version:
description: 'Version of wasm-opt to install'
default: '116'
runs:
using: 'composite'
steps:
- name: Check platform compatibility
run: |
if [[ "$(uname)" != "Linux" ]]; then
echo "Error: This action is only compatible with Linux."
exit 1
fi
shell: bash
- name: Download wasm-opt
run: |
set -e
SOURCE="https://github.com/WebAssembly/binaryen/releases/download/version_${{ inputs.version }}/binaryen-version_${{ inputs.version }}-x86_64-linux.tar.gz"
TEMP_ARCHIVE="$RUNNER_TEMP/binaryen-version_${{ inputs.version }}-x86_64-linux.tar.gz"
curl -L -o "$TEMP_ARCHIVE" "$SOURCE"
tar -xvzf $TEMP_ARCHIVE -C $RUNNER_TEMP
echo "$RUNNER_TEMP/binaryen-version_${{ inputs.version }}/bin" >> $GITHUB_PATH
shell: bash
id: install-binary
- name: Verify installation
run: |
if ! command -v wasm-opt &> /dev/null; then
echo "Error: wasm-opt binary was not installed successfully."
exit 1
fi
shell: bash
id: verify-installation
@@ -1,16 +1,16 @@
name: build-upload-binaries
name: Build and upload binaries to artifact storage
on:
workflow_dispatch:
inputs:
inputs:
add_tokio_unstable:
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
required: true
default: false
type: boolean
type: boolean
env:
NETWORK: mainnet
NETWORK: mainnet
jobs:
publish-nym:
+1 -1
View File
@@ -9,7 +9,7 @@ on:
jobs:
build:
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v3
- name: Install rsync
+6 -6
View File
@@ -31,8 +31,8 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-linux]
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
@@ -45,12 +45,12 @@ jobs:
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Branch name
run: echo running on branch ${GITHUB_REF##*/}
- name: Run tests against binaries
run: ./build_and_run.sh ${{ github.head_ref || github.ref_name }}
working-directory: tests/
+4 -5
View File
@@ -1,14 +1,13 @@
name: ci-build-ts
on:
pull_request:
push:
paths:
- "ts-packages/**"
- "sdk/typescript/**"
- 'ts-packages/**'
jobs:
build:
runs-on: ubuntu-20.04-16-core
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
@@ -21,7 +20,7 @@ jobs:
- name: Setup yarn
run: npm install -g yarn
- name: Build
run: yarn && yarn build && yarn build:ci:storybook
run: yarn && yarn build && yarn build:ci
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
+21 -1
View File
@@ -2,6 +2,20 @@ name: ci-build-upload-binaries
on:
workflow_dispatch:
push:
paths:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- 'nym-api/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/ts-rs-cli/**'
pull_request:
paths:
- 'clients/**'
@@ -17,6 +31,9 @@ on:
- 'tools/nym-cli/**'
- 'tools/ts-rs-cli/**'
env:
NETWORK: mainnet
jobs:
publish-nym:
strategy:
@@ -27,6 +44,8 @@ jobs:
runs-on: ${{ matrix.platform }}
env:
CARGO_TERM_COLOR: always
# a push event from the origin repo, or a PR from external repo
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'nymtech/nym' }}
steps:
- uses: actions/checkout@v3
@@ -40,7 +59,8 @@ jobs:
echo $OUTPUT_DIR
- name: Install Dependencies (Linux)
run: sudo apt update && sudo apt install libudev-dev
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
+3
View File
@@ -48,6 +48,9 @@ jobs:
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
# Enable sccache via environment variable
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
+5 -1
View File
@@ -2,6 +2,10 @@ name: ci-contracts-schema
on:
workflow_dispatch:
push:
paths:
- 'contracts/**'
- 'common/**'
pull_request:
paths:
- 'contracts/**'
@@ -10,7 +14,7 @@ on:
jobs:
check-schema:
name: Generate and check schema
runs-on: custom-linux
runs-on: custom-runner-linux
env:
CARGO_TERM_COLOR: always
steps:
@@ -2,6 +2,10 @@ name: ci-contracts-upload-binaries
on:
workflow_dispatch:
push:
paths:
- 'common/**'
- 'contracts/**'
pull_request:
paths:
- 'common/**'
@@ -20,6 +24,8 @@ jobs:
runs-on: ${{ matrix.platform }}
env:
CARGO_TERM_COLOR: always
# a push event from the origin repo, or a PR from external repo
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'nymtech/nym' }}
steps:
- uses: actions/checkout@v3
@@ -32,6 +38,10 @@ jobs:
mkdir -p $OUTPUT_DIR
echo $OUTPUT_DIR
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -40,9 +50,7 @@ jobs:
override: true
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '112'
run: cargo install --version 0.112.0 wasm-opt
- name: Build release contracts
run: make contracts
+5 -10
View File
@@ -9,7 +9,7 @@ on:
jobs:
build:
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v3
- name: Install rsync
@@ -28,20 +28,15 @@ jobs:
command: build
args: --workspace --release --all
- name: Install mdbook
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook)
- name: Install mdbook plugins
run: |
cargo install --vers "=0.2.2" mdbook-variables && cargo install \
--vers "^1.8.0" mdbook-admonish --force && cargo install --vers \
--vers "^1.8.0" mdbook-admonish && 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 \
# && cd documentation \
# && mdbook-admonish install dev-portal \
# && mdbook-admonish install docs \
# && mdbook-admonish install operators
&& cargo install --vers "^0.7.7" mdbook-linkcheck
- name: Build all projects in documentation/ & move to ~/dist/docs/
run: cd documentation && ./build_all_to_dist.sh
run: cd documentation && ./build_all_to_dist.sh
continue-on-error: false
- name: Deploy branch to CI www
continue-on-error: true
+11 -8
View File
@@ -1,6 +1,15 @@
name: ci-lint-typescript
on:
push:
paths:
- "ts-packages/**"
- "sdk/typescript/**"
- "nym-connect/desktop/src/**"
- "nym-connect/desktop/package.json"
- "nym-wallet/src/**"
- "nym-wallet/package.json"
- "explorer/**"
pull_request:
paths:
- "ts-packages/**"
@@ -13,7 +22,7 @@ on:
jobs:
build:
runs-on: ubuntu-20.04-16-core
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- uses: rlespinasse/github-slug-action@v3.x
@@ -28,15 +37,9 @@ jobs:
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
- name: Set up Go
uses: actions/setup-go@v4
with:
@@ -46,7 +49,7 @@ jobs:
run: yarn
- name: Build packages
run: yarn build:ci
run: yarn build:ci:sdk
- name: Lint
run: yarn lint
@@ -1,6 +1,16 @@
name: ci-nym-connect-desktop-rust
on:
push:
paths:
- "nym-connect/desktop/src-tauri/**"
- "nym-connect/desktop/src-tauri/Cargo.toml"
- "clients/client-core/**"
- "clients/socks5/**"
- "common/**"
- "gateway/gateway-requests/**"
- "contracts/vesting/**"
- "nym-api/nym-api-requests/**"
pull_request:
paths:
- "nym-connect/desktop/src-tauri/**"
@@ -17,6 +27,8 @@ jobs:
runs-on: [self-hosted, custom-linux]
env:
CARGO_TERM_COLOR: always
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
+2 -2
View File
@@ -1,7 +1,7 @@
name: ci-nym-connect-desktop
on:
pull_request:
push:
paths:
- 'nym-connect/desktop/**'
@@ -11,7 +11,7 @@ defaults:
jobs:
build:
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
@@ -12,7 +12,7 @@ defaults:
jobs:
build:
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
-44
View File
@@ -1,44 +0,0 @@
name: ci-nym-vpn-ui-js
on:
push:
paths:
- 'nym-vpn/ui/src/**'
- 'nym-vpn/ui/package.json'
- 'nym-vpn/ui/index.html'
pull_request:
paths:
- 'nym-vpn/ui/src/**'
- 'nym-vpn/ui/package.json'
- 'nym-vpn/ui/index.html'
jobs:
check:
runs-on: [ self-hosted, custom-linux ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Yarn
run: npm install -g yarn
- name: Install dependencies
working-directory: nym-vpn/ui
run: yarn
- name: Type-check
working-directory: nym-vpn/ui
run: yarn typecheck
- name: Check lint
working-directory: nym-vpn/ui
run: yarn lint
- name: Check formatting
working-directory: nym-vpn/ui
run: yarn fmt:check
# - name: Run tests
# working-directory: nym-vpn/ui
# run: yarn test
- name: Check build
working-directory: nym-vpn/ui
run: yarn build
-64
View File
@@ -1,64 +0,0 @@
name: ci-nym-vpn-ui-rust
on:
push:
paths:
- 'nym-vpn/ui/src-tauri/**'
pull_request:
paths:
- 'nym-vpn/ui/src-tauri/**'
jobs:
build:
runs-on: [self-hosted, custom-linux]
env:
CARGO_TERM_COLOR: always
CARGOTOML_PATH: ./nym-vpn/ui/src-tauri/Cargo.toml
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
continue-on-error: true
- name: Checkout
uses: actions/checkout@v4
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Prepare build
working-directory: nym-vpn/ui/
run: mkdir dist
- name: Check build
working-directory: nym-vpn/ui/src-tauri
run: cargo build --release --lib --features custom-protocol
# - name: Run all tests
# uses: actions-rs/cargo@v1
# with:
# command: test
# args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace --all-features -- -D warnings
+2
View File
@@ -19,6 +19,8 @@ jobs:
runs-on: [ self-hosted, custom-linux ]
env:
CARGO_TERM_COLOR: always
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
+4 -15
View File
@@ -1,44 +1,35 @@
name: ci-nym-wallet-storybook
name: Nym Wallet Storybook
on:
pull_request:
push:
paths:
- 'nym-wallet/**'
jobs:
build:
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build dependencies
run: yarn && yarn build
- name: Build storybook
run: yarn storybook:build
working-directory: ./nym-wallet
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
@@ -50,11 +41,9 @@ jobs:
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: nym-wallet
+5 -9
View File
@@ -1,6 +1,10 @@
name: ci-sdk-docs-typescript
on:
push:
paths:
- "sdk/typescript/**"
- "wasm/**"
pull_request:
paths:
- "sdk/typescript/**"
@@ -8,7 +12,7 @@ on:
jobs:
build:
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
@@ -30,14 +34,6 @@ jobs:
with:
go-version: '1.20'
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
- name: Build branch WASM packages
run: make sdk-wasm-build
+4 -5
View File
@@ -9,7 +9,7 @@ on:
jobs:
wasm:
runs-on: [custom-linux]
runs-on: [custom-runner-linux]
env:
CARGO_TERM_COLOR: always
steps:
@@ -18,7 +18,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: actions-rs/toolchain@v1
with:
profile: minimal
@@ -32,13 +32,12 @@ jobs:
with:
go-version: '1.20'
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
run: cargo install wasm-opt
- name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli
+1 -1
View File
@@ -1,4 +1,4 @@
name: greetings
name: Greetings
on: [pull_request_target, issues]
+1 -1
View File
@@ -70,7 +70,7 @@ jobs:
notification:
needs: build
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
@@ -25,7 +25,7 @@ jobs:
- 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'
if: matrix.os == 'custom-linux'
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
@@ -35,10 +35,6 @@ 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:
@@ -72,7 +68,7 @@ jobs:
notification:
needs: build
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
+2 -2
View File
@@ -1,4 +1,4 @@
name: nightly-security-audit
name: Daily security audit
on:
schedule:
@@ -26,7 +26,7 @@ jobs:
path: .github/workflows/support-files/notifications/deny.message
notification:
needs: cargo-deny
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- name: Check out repository code
uses: actions/checkout@v2
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [macos-12-large]
platform: [macos-latest]
runs-on: ${{ matrix.platform }}
outputs:
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [macos-12-large]
platform: [macos-latest]
runs-on: ${{ matrix.platform }}
outputs:
@@ -1,4 +1,4 @@
name: publish-nyms5-android-apk
name: Nyms5 Android
# unsigned APKs only, supported archs:
# - arm64-v8a (arm64)
# - x86_64
@@ -94,7 +94,7 @@ jobs:
gh-release:
name: Publish APK (GH release)
needs: build
runs-on: custom-linux
runs-on: custom-runner-linux
steps:
- name: Checkout
uses: actions/checkout@v3
+1 -4
View File
@@ -4,7 +4,7 @@ on:
jobs:
publish:
runs-on: ubuntu-20.04-16-core
runs-on: [custom-ubuntu-20.04]
steps:
- uses: actions/checkout@v2
@@ -25,9 +25,6 @@ jobs:
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
run: cargo install wasm-opt
- name: Install dependencies
run: yarn
Generated
+66 -229
View File
@@ -799,6 +799,18 @@ dependencies = [
"tower-service",
]
[[package]]
name = "axum-macros"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "backtrace"
version = "0.3.69"
@@ -2317,7 +2329,7 @@ dependencies = [
"hashbrown 0.14.1",
"lock_api",
"once_cell",
"parking_lot_core 0.9.9",
"parking_lot_core 0.9.8",
]
[[package]]
@@ -2902,7 +2914,7 @@ dependencies = [
"unsigned-varint",
"utoipa",
"utoipa-swagger-ui",
"uuid 1.5.0",
"uuid 1.4.1",
]
[[package]]
@@ -2994,7 +3006,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -3855,20 +3867,6 @@ dependencies = [
"itoa",
]
[[package]]
name = "http-api-client"
version = "0.1.0"
dependencies = [
"async-trait",
"reqwest",
"serde",
"serde_json",
"thiserror",
"tracing",
"url",
"wasmtimer",
]
[[package]]
name = "http-body"
version = "0.4.5"
@@ -3880,12 +3878,6 @@ dependencies = [
"pin-project-lite 0.2.13",
]
[[package]]
name = "http-range-header"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
[[package]]
name = "httparse"
version = "1.8.0"
@@ -4021,16 +4013,16 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.58"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
"windows 0.48.0",
]
[[package]]
@@ -4112,7 +4104,7 @@ checksum = "bfbcff6ae46750b15cc594bfd277b188cbddcfdc1817848f97f03f26f8625b9e"
dependencies = [
"cfg-if",
"js-sys",
"uuid 1.5.0",
"uuid 1.4.1",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
@@ -5369,9 +5361,9 @@ checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1"
[[package]]
name = "lock_api"
version = "0.4.11"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg 1.1.0",
"scopeguard",
@@ -5559,16 +5551,15 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"async-trait",
"futures",
"http-api-client",
"js-sys",
"nym-ordered-buffer",
"nym-service-providers-common",
"nym-socks5-requests",
"rand 0.7.3",
"reqwest",
"serde",
"serde-wasm-bindgen",
"thiserror",
@@ -5995,7 +5986,6 @@ dependencies = [
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-name-service-common",
"nym-node-requests",
"nym-node-tester-utils",
"nym-pemstore",
"nym-service-provider-directory-common",
@@ -6006,6 +5996,7 @@ dependencies = [
"nym-vesting-contract-common",
"okapi",
"pin-project",
"pretty_env_logger",
"rand 0.7.3",
"rand 0.8.5",
"reqwest",
@@ -6025,7 +6016,7 @@ dependencies = [
"tokio-stream",
"ts-rs",
"url",
"uuid 1.5.0",
"uuid 1.4.1",
"zeroize",
]
@@ -6039,7 +6030,6 @@ dependencies = [
"getset",
"nym-coconut-interface",
"nym-mixnet-contract-common",
"nym-node-requests",
"schemars",
"serde",
"ts-rs",
@@ -6073,14 +6063,12 @@ dependencies = [
"opentelemetry",
"opentelemetry-jaeger",
"pretty_env_logger",
"schemars",
"semver 0.11.0",
"serde",
"serde_json",
"tracing-opentelemetry",
"tracing-subscriber",
"tracing-tree",
"utoipa",
"vergen",
]
@@ -6259,7 +6247,7 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"anyhow",
"futures",
@@ -6477,18 +6465,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "nym-exit-policy"
version = "0.1.0"
dependencies = [
"reqwest",
"serde",
"serde_json",
"thiserror",
"tracing",
"utoipa",
]
[[package]]
name = "nym-explorer-api-requests"
version = "0.1.0"
@@ -6521,6 +6497,9 @@ dependencies = [
"anyhow",
"async-trait",
"atty",
"axum",
"axum-macros",
"base64 0.21.4",
"bip39",
"bs58 0.4.0",
"clap 4.4.6",
@@ -6528,7 +6507,9 @@ dependencies = [
"dashmap",
"dirs 4.0.0",
"dotenvy",
"fastrand 2.0.1",
"futures",
"hmac 0.12.1",
"humantime-serde",
"hyper",
"lazy_static",
@@ -6544,7 +6525,6 @@ dependencies = [
"nym-mixnode-common",
"nym-network-defaults",
"nym-network-requester",
"nym-node",
"nym-pemstore",
"nym-sphinx",
"nym-statistics-common",
@@ -6558,6 +6538,7 @@ dependencies = [
"rand 0.8.5",
"serde",
"serde_json",
"sha2 0.10.8",
"sqlx 0.5.13",
"subtle-encoding",
"thiserror",
@@ -6567,6 +6548,7 @@ dependencies = [
"tokio-util",
"tower",
"url",
"x25519-dalek 2.0.0",
"zeroize",
]
@@ -6634,6 +6616,21 @@ dependencies = [
"serde",
]
[[package]]
name = "nym-http-requests"
version = "0.1.0"
dependencies = [
"bytecodec",
"bytes",
"http",
"httpcodec",
"nym-ordered-buffer",
"nym-service-providers-common",
"nym-socks5-requests",
"thiserror",
"url",
]
[[package]]
name = "nym-inclusion-probability"
version = "0.1.0"
@@ -6815,7 +6812,6 @@ dependencies = [
"nym-config",
"nym-credential-storage",
"nym-crypto",
"nym-exit-policy",
"nym-network-defaults",
"nym-ordered-buffer",
"nym-sdk",
@@ -6859,57 +6855,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "nym-node"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"bytes",
"colored",
"dashmap",
"fastrand 2.0.1",
"hmac 0.12.1",
"hyper",
"mime",
"nym-config",
"nym-crypto",
"nym-node-requests",
"nym-task",
"nym-wireguard-types",
"rand 0.7.3",
"serde",
"serde_json",
"serde_yaml",
"thiserror",
"tokio",
"tower",
"tower-http",
"tracing",
"utoipa",
"utoipa-swagger-ui",
"x25519-dalek 2.0.0",
]
[[package]]
name = "nym-node-requests"
version = "0.1.0"
dependencies = [
"async-trait",
"base64 0.21.4",
"http-api-client",
"nym-bin-common",
"nym-crypto",
"nym-exit-policy",
"nym-wireguard-types",
"schemars",
"serde",
"serde_json",
"thiserror",
"tokio",
"utoipa",
]
[[package]]
name = "nym-node-tester-utils"
version = "0.1.0"
@@ -6931,7 +6876,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"futures",
"js-sys",
@@ -7192,7 +7137,6 @@ version = "0.1.0"
dependencies = [
"bincode",
"log",
"nym-exit-policy",
"nym-service-providers-common",
"nym-sphinx-addressing",
"serde",
@@ -7400,7 +7344,6 @@ dependencies = [
"async-trait",
"bs58 0.4.0",
"log",
"nym-api-requests",
"nym-bin-common",
"nym-config",
"nym-crypto",
@@ -7422,16 +7365,13 @@ dependencies = [
name = "nym-types"
version = "1.0.0"
dependencies = [
"base64 0.21.4",
"cosmrs",
"cosmwasm-std",
"eyre",
"hmac 0.12.1",
"itertools 0.11.0",
"itertools 0.10.5",
"log",
"nym-coconut-interface",
"nym-config",
"nym-crypto",
"nym-mixnet-contract-common",
"nym-validator-client",
"nym-vesting-contract-common",
@@ -7439,13 +7379,11 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"sha2 0.10.8",
"strum 0.25.0",
"strum 0.23.0",
"tempfile",
"thiserror",
"ts-rs",
"url",
"x25519-dalek 2.0.0",
]
[[package]]
@@ -7466,7 +7404,6 @@ dependencies = [
"eyre",
"flate2",
"futures",
"http-api-client",
"itertools 0.10.5",
"log",
"nym-api-requests",
@@ -7533,7 +7470,7 @@ dependencies = [
[[package]]
name = "nym-wasm-sdk"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"mix-fetch-wasm",
"nym-client-wasm",
@@ -7555,33 +7492,12 @@ dependencies = [
"ip_network_table",
"log",
"nym-task",
"nym-wireguard-types",
"rand 0.8.5",
"serde",
"tap",
"thiserror",
"tokio",
"tokio-tun",
]
[[package]]
name = "nym-wireguard-types"
version = "0.1.0"
dependencies = [
"base64 0.21.4",
"boringtun",
"dashmap",
"hmac 0.12.1",
"nym-crypto",
"rand 0.7.3",
"serde",
"serde_json",
"sha2 0.10.8",
"thiserror",
"utoipa",
"x25519-dalek 2.0.0",
]
[[package]]
name = "object"
version = "0.32.1"
@@ -7858,9 +7774,9 @@ dependencies = [
[[package]]
name = "parking"
version = "2.2.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067"
[[package]]
name = "parking_lot"
@@ -7880,7 +7796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core 0.9.9",
"parking_lot_core 0.9.8",
]
[[package]]
@@ -7899,13 +7815,13 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.9"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.4.1",
"redox_syscall 0.3.5",
"smallvec",
"windows-targets 0.48.5",
]
@@ -8786,15 +8702,6 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_users"
version = "0.4.3"
@@ -8872,13 +8779,13 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.2"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
checksum = "aaac441002f822bc9705a681810a4dd2963094b9ca0ddc41cb963a4c189189ea"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.3",
"regex-automata 0.4.2",
"regex-syntax 0.8.2",
]
@@ -8893,9 +8800,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
checksum = "5011c7e263a695dc8ca064cddb722af1be54e517a280b12a5356f98366899e5d"
dependencies = [
"aho-corasick",
"memchr",
@@ -9760,19 +9667,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
dependencies = [
"indexmap 2.0.2",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sha-1"
version = "0.9.8"
@@ -10339,15 +10233,6 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros 0.25.3",
]
[[package]]
name = "strum_macros"
version = "0.23.1"
@@ -10374,19 +10259,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.38",
]
[[package]]
name = "stun"
version = "0.4.4"
@@ -10907,9 +10779,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [
"futures-util",
"log",
"native-tls",
"tokio",
"tokio-native-tls",
"tungstenite",
]
@@ -11070,31 +10940,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-http"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
dependencies = [
"bitflags 2.4.1",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite 0.2.13",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
@@ -11362,7 +11207,6 @@ dependencies = [
"http",
"httparse",
"log",
"native-tls",
"rand 0.8.5",
"sha1",
"thiserror",
@@ -11525,12 +11369,6 @@ dependencies = [
"subtle 2.4.1",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]]
name = "unsigned-varint"
version = "0.7.2"
@@ -11640,7 +11478,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84614caa239fb25b2bb373a52859ffd94605ceb256eeb1d63436325cf81e3653"
dependencies = [
"actix-web",
"axum",
"mime_guess",
"regex",
"rust-embed",
@@ -11658,9 +11495,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
[[package]]
name = "uuid"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
dependencies = [
"getrandom 0.2.10",
"serde",
@@ -12103,7 +11940,7 @@ dependencies = [
"tokio",
"turn",
"url",
"uuid 1.5.0",
"uuid 1.4.1",
"waitgroup",
"webrtc-mdns",
"webrtc-util",
+4 -16
View File
@@ -46,8 +46,7 @@ members = [
"common/crypto",
"common/dkg",
"common/execute",
"common/exit-policy",
"common/http-api-client",
"common/http-requests",
"common/inclusion-probability",
"common/ledger",
"common/mixnode-common",
@@ -79,7 +78,6 @@ members = [
"common/wasm/storage",
"common/wasm/utils",
"common/wireguard",
"common/wireguard-types",
"explorer-api",
"explorer-api/explorer-api-requests",
"explorer-api/explorer-client",
@@ -95,8 +93,6 @@ members = [
"nym-api",
"nym-browser-extension/storage",
"nym-api/nym-api-requests",
"nym-node",
"nym-node/nym-node-requests",
"nym-outfox",
"tools/internal/ssl-inject",
"tools/internal/sdk-version-bump",
@@ -120,7 +116,7 @@ default-members = [
"explorer-api",
]
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles"]
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "cpu-cycles"]
[workspace.package]
authors = ["Nym Technologies SA"]
@@ -133,10 +129,7 @@ license = "Apache-2.0"
[workspace.dependencies]
anyhow = "1.0.71"
async-trait = "0.1.68"
axum = "0.6.20"
base64 = "0.21.4"
bip39 = { version = "2.0.0", features = ["zeroize"] }
boringtun = { git = "https://github.com/cloudflare/boringtun", rev = "e1d6360d6ab4529fc942a078e4c54df107abe2ba" }
cfg-if = "1.0.0"
cosmwasm-derive = "=1.3.0"
cosmwasm-schema = "=1.3.0"
@@ -158,27 +151,22 @@ dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7"
getrandom = "0.2.10"
hyper = "0.14.27"
k256 = "0.13"
lazy_static = "1.4.0"
log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = "0.11.22"
schemars = "0.8.1"
reqwest = "0.11.18"
serde = "1.0.152"
serde_json = "1.0.91"
tap = "1.0.1"
tendermint-rpc = "0.32" # same version as used by cosmrs
thiserror = "1.0.48"
thiserror = "1.0.38"
tokio = "1.24.1"
tokio-tungstenite = "0.20.1"
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
utoipa = "3.5.0"
utoipa-swagger-ui = "3.1.5"
url = "2.4"
zeroize = "1.6.0"
+3 -3
View File
@@ -50,10 +50,10 @@ Node, node operator and delegator rewards are determined according to the princi
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda_{i}#gh-dark-mode-only">|ratio of stake operator has pledged to their node to the token circulating supply.
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\omega_{i}#gh-dark-mode-only">|fraction of total effort undertaken by node `i`, set to `1/k`.
|<img src="https://render.githubusercontent.com/render/math?math=k#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}k#gh-dark-mode-only">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `reward set` size, and set to 720 in testnet Sandbox.
|<img src="https://render.githubusercontent.com/render/math?math=\alpha#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\alpha#gh-dark-mode-only">|A Sybil attack resistance parameter - the higher this parameter is set, the stronger the reduction in competitiveness for a Sybil attacker.
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PM_{i}#gh-dark-mode-only">|declared profit margin of operator `i`, defaults to 10%.
|<img src="https://render.githubusercontent.com/render/math?math=\alpha#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\alpha#gh-dark-mode-only">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitiveness gets for a Sybil attacker.
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PM_{i}#gh-dark-mode-only">|declared profit margin of operator `i`, defaults to 10% in.
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PF_{i}#gh-dark-mode-only">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PP_{i}#gh-dark-mode-only">|cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMs.
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PP_{i}#gh-dark-mode-only">|cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMT.
Node reward for node `i` is determined as:
@@ -11,7 +11,6 @@ use crate::{
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseConfigV1_1_20_2;
use nym_client_core::config::old_config_v1_1_30::ConfigV1_1_30 as BaseConfigV1_1_30;
use nym_client_core::config::GatewayEndpointConfig;
use nym_config::read_config_from_toml_file;
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
@@ -52,7 +51,7 @@ impl ConfigV1_1_20_2 {
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), ClientError> {
let gateway_details = self.base.client.gateway_endpoint.clone().into();
let config = Config {
base: BaseConfigV1_1_30::from(self.base).into(),
base: self.base.into(),
socket: self.socket.into(),
storage_paths: ClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
+3 -13
View File
@@ -22,7 +22,6 @@ use nym_sphinx::addressing::clients::Recipient;
use nym_topology::NymTopology;
use serde::Serialize;
use std::fmt::Display;
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::{fs, io};
use tap::TapFallible;
@@ -78,10 +77,6 @@ pub(crate) struct Init {
#[clap(short, long)]
port: Option<u16>,
/// The custom host on which the socks5 client will be listening 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>,
@@ -108,7 +103,6 @@ impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
nym_apis: init_config.nym_apis,
ip: init_config.host,
port: init_config.port,
use_anonymous_replies: init_config.use_reply_surbs,
fastmode: init_config.fastmode,
@@ -126,7 +120,7 @@ impl From<Init> for OverrideConfig {
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::types::InitResults,
socks5_listening_address: SocketAddr,
socks5_listening_port: u16,
client_address: String,
}
@@ -138,7 +132,7 @@ impl InitResults {
address,
gateway,
),
socks5_listening_address: config.core.socks5.bind_adddress,
socks5_listening_port: config.core.socks5.listening_port,
client_address: address.to_string(),
}
}
@@ -147,11 +141,7 @@ impl InitResults {
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.client_core)?;
writeln!(
f,
"SOCKS5 listening address: {}",
self.socks5_listening_address
)?;
writeln!(f, "SOCKS5 listening port: {}", self.socks5_listening_port)?;
write!(f, "Address of this client: {}", self.client_address)
}
}
+11 -35
View File
@@ -4,8 +4,7 @@
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::config::{BaseClientConfig, Config, SocksClientPaths};
use crate::config::{BaseClientConfig, Config};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
@@ -23,7 +22,6 @@ use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
use std::error::Error;
use std::net::IpAddr;
pub(crate) mod build_info;
pub mod init;
@@ -74,7 +72,6 @@ pub(crate) enum Commands {
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
nym_apis: Option<Vec<url::Url>>,
ip: Option<IpAddr>,
port: Option<u16>,
use_anonymous_replies: Option<bool>,
fastmode: bool,
@@ -148,7 +145,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
.with_optional(Config::with_port, args.port)
.with_optional(Config::with_ip, args.ip)
.with_optional_base_custom_env(
BaseClientConfig::with_custom_nym_apis,
args.nym_apis,
@@ -168,11 +164,12 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
}
fn persist_gateway_details(
storage_paths: &SocksClientPaths,
config: &Config,
details: GatewayEndpointConfig,
) -> Result<(), Socks5ClientError> {
let details_store = OnDiskGatewayDetails::new(&storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(storage_paths.common_paths.keys.clone());
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
@@ -202,10 +199,9 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
let updated_step1: ConfigV1_1_20 = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated_step3, gateway_config) = updated_step2.upgrade()?;
persist_gateway_details(&updated_step3.storage_paths, gateway_config)?;
let (updated, gateway_config) = updated_step2.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let updated: Config = updated_step3.into();
updated.save_to_default_location()?;
Ok(true)
}
@@ -223,10 +219,9 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated_step2, gateway_config) = updated_step1.upgrade()?;
persist_gateway_details(&updated_step2.storage_paths, gateway_config)?;
let (updated, gateway_config) = updated_step1.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let updated: Config = updated_step2.into();
updated.save_to_default_location()?;
Ok(true)
}
@@ -241,25 +236,9 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
info!("It seems the client is using <= v1.1.20_2 config template.");
info!("It is going to get updated to the current specification.");
let (updated_step1, gateway_config) = old_config.upgrade()?;
persist_gateway_details(&updated_step1.storage_paths, gateway_config)?;
let (updated, gateway_config) = old_config.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let updated: Config = updated_step1.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.30 (which is incompatible with the current one, i.e. +1.1.31)
let Ok(old_config) = ConfigV1_1_30::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 client 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()?;
Ok(true)
}
@@ -274,9 +253,6 @@ fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_30_config(id)? {
return Ok(());
}
Ok(())
}
-6
View File
@@ -15,7 +15,6 @@ 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)]
@@ -54,10 +53,6 @@ pub(crate) struct Run {
#[clap(short, long)]
port: Option<u16>,
/// The custom host on which the socks5 client will be listening for requests
#[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>,
@@ -93,7 +88,6 @@ impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nym_apis: run_config.nym_apis,
ip: run_config.host,
port: run_config.port,
use_anonymous_replies: run_config.use_anonymous_replies,
fastmode: run_config.fastmode,
+2 -11
View File
@@ -1,6 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
@@ -10,18 +11,15 @@ use nym_config::{
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::io;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::str::FromStr;
pub use crate::config::persistence::SocksClientPaths;
pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_socks5_client_core::config::Config as CoreConfig;
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
mod persistence;
mod template;
@@ -104,15 +102,8 @@ impl Config {
self.core.validate()
}
#[must_use]
pub fn with_port(mut self, port: u16) -> Self {
self.core = self.core.with_port(port);
self
}
#[must_use]
pub fn with_ip(mut self, ip: IpAddr) -> Self {
self.core = self.core.with_ip(ip);
self.core.socks5.listening_port = port;
self
}
@@ -1,11 +1,11 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::{
config::{default_config_filepath, persistence::SocksClientPaths},
config::{default_config_filepath, persistence::SocksClientPaths, Config},
error::Socks5ClientError,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::GatewayEndpointConfig;
@@ -43,9 +43,9 @@ impl ConfigV1_1_20_2 {
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> Result<(ConfigV1_1_30, GatewayEndpointConfig), Socks5ClientError> {
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), Socks5ClientError> {
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
let config = ConfigV1_1_30 {
let config = Config {
core: self.core.into(),
storage_paths: SocksClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
@@ -1,44 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::{default_config_filepath, Config};
use nym_bin_common::logging::LoggingSettings;
use nym_config::read_config_from_toml_file;
use nym_socks5_client_core::config::old_config_v1_1_30::ConfigV1_1_30 as CoreConfigV1_1_30;
use serde::{Deserialize, Serialize};
use std::io;
use std::path::Path;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_30 {
pub core: CoreConfigV1_1_30,
// I'm leaving a landmine here for when the paths actually do change the next time,
// but propagating the change right now (in ALL clients) would be such a hassle...,
// so sorry for the next person looking at it : )
pub storage_paths: SocksClientPaths,
pub logging: LoggingSettings,
}
impl From<ConfigV1_1_30> for Config {
fn from(value: ConfigV1_1_30) -> Self {
Config {
core: value.core.into(),
storage_paths: value.storage_paths,
logging: LoggingSettings::default(),
}
}
}
impl ConfigV1_1_30 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
}
+2 -3
View File
@@ -75,9 +75,8 @@ gateway_details = '{{ storage_paths.gateway_details }}'
# The mix address of the provider to which all requests are going to be sent.
provider_mix_address = '{{ core.socks5.provider_mix_address }}'
# The address on which the client will be listening for incoming requests
# (default: 127.0.0.1:1080)
bind_adddress = '{{ core.socks5.bind_adddress }}'
# The port on which the client will be listening for incoming requests
listening_port = {{ core.socks5.listening_port }}
# 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
+1 -1
View File
@@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
bip39 = { workspace = true }
rand = "0.7.3"
thiserror = { workspace = true }
thiserror = "1.0"
url = { workspace = true }
nym-coconut-interface = { path = "../coconut-interface" }
-4
View File
@@ -15,7 +15,6 @@ clap_complete_fig = "4.0"
log = { workspace = true }
pretty_env_logger = "0.4.0"
semver = "0.11"
schemars = { workspace = true, features = ["preserve_order"], optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
@@ -30,7 +29,6 @@ opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
"isahc_collector_client",
] }
tracing-opentelemetry = { version = "0.19.0", optional = true }
utoipa = { workspace = true, optional = true }
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
@@ -44,9 +42,7 @@ vergen = { version = "=7.4.3", default-features = false, features = [
[features]
default = []
openapi = ["utoipa"]
output_format = ["serde_json"]
bin_info_schema = ["schemars"]
tracing = [
"tracing-subscriber",
"tracing-tree",
@@ -81,8 +81,6 @@ impl BinaryBuildInformation {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "bin_info_schema", derive(schemars::JsonSchema))]
pub struct BinaryBuildInformationOwned {
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
pub binary_name: String,
@@ -69,7 +69,7 @@ impl NymApiTopologyProvider {
Ok(mixes) => mixes,
};
let gateways = match self.validator_client.get_cached_described_gateways().await {
let gateways = match self.validator_client.get_cached_gateways().await {
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
+20 -16
View File
@@ -1,7 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError};
use nym_config::defaults::NymNetworkDetails;
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::GatewayConfig;
@@ -13,6 +12,7 @@ use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
@@ -20,7 +20,6 @@ pub mod disk_persistence;
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
@@ -281,24 +280,29 @@ impl GatewayEndpointConfig {
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
}
pub fn from_node(
node: nym_topology::gateway::Node,
must_use_tls: bool,
) -> Result<Self, ClientCoreError> {
let gateway_listener = if must_use_tls {
node.clients_address_tls()
.ok_or(ClientCoreError::UnsupportedWssProtocol {
gateway: node.identity_key.to_base58_string(),
})?
pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self {
// TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct
if use_tls {
Self::from_topology_node_tls(node)
} else {
node.clients_address()
};
Self::from_topology_node_no_tls(node)
}
}
Ok(GatewayEndpointConfig {
pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener,
gateway_listener: node.clients_address(),
gateway_owner: node.owner,
})
}
}
pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address_tls(),
gateway_owner: node.owner,
}
}
}
@@ -1,11 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_30::{
AcknowledgementsV1_1_30, ClientV1_1_30, ConfigV1_1_30, CoverTrafficV1_1_30, DebugConfigV1_1_30,
GatewayConnectionV1_1_30, ReplySurbsV1_1_30, TopologyV1_1_30, TrafficV1_1_30,
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection,
GatewayEndpointConfig, ReplySurbs, Topology, Traffic,
};
use crate::config::GatewayEndpointConfig;
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@@ -59,9 +58,9 @@ pub struct ConfigV1_1_20_2 {
pub debug: DebugConfigV1_1_20_2,
}
impl From<ConfigV1_1_20_2> for ConfigV1_1_30 {
impl From<ConfigV1_1_20_2> for Config {
fn from(value: ConfigV1_1_20_2) -> Self {
ConfigV1_1_30 {
Config {
client: value.client.into(),
debug: value.debug.into(),
}
@@ -108,9 +107,9 @@ pub struct ClientV1_1_20_2 {
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
}
impl From<ClientV1_1_20_2> for ClientV1_1_30 {
impl From<ClientV1_1_20_2> for Client {
fn from(value: ClientV1_1_20_2) -> Self {
ClientV1_1_30 {
Client {
version: value.version,
id: value.id,
disabled_credentials_mode: value.disabled_credentials_mode,
@@ -133,9 +132,9 @@ pub struct TrafficV1_1_20_2 {
pub packet_type: PacketType,
}
impl From<TrafficV1_1_20_2> for TrafficV1_1_30 {
impl From<TrafficV1_1_20_2> for Traffic {
fn from(value: TrafficV1_1_20_2) -> Self {
TrafficV1_1_30 {
Traffic {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -169,9 +168,9 @@ pub struct CoverTrafficV1_1_20_2 {
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTrafficV1_1_20_2> for CoverTrafficV1_1_30 {
impl From<CoverTrafficV1_1_20_2> for CoverTraffic {
fn from(value: CoverTrafficV1_1_20_2) -> Self {
CoverTrafficV1_1_30 {
CoverTraffic {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
@@ -196,9 +195,9 @@ pub struct GatewayConnectionV1_1_20_2 {
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV1_1_20_2> for GatewayConnectionV1_1_30 {
impl From<GatewayConnectionV1_1_20_2> for GatewayConnection {
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
GatewayConnectionV1_1_30 {
GatewayConnection {
gateway_response_timeout: value.gateway_response_timeout,
}
}
@@ -222,9 +221,9 @@ pub struct AcknowledgementsV1_1_20_2 {
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV1_1_20_2> for AcknowledgementsV1_1_30 {
impl From<AcknowledgementsV1_1_20_2> for Acknowledgements {
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
AcknowledgementsV1_1_30 {
Acknowledgements {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
@@ -262,9 +261,9 @@ impl Default for TopologyV1_1_20_2 {
}
}
impl From<TopologyV1_1_20_2> for TopologyV1_1_30 {
impl From<TopologyV1_1_20_2> for Topology {
fn from(value: TopologyV1_1_20_2) -> Self {
TopologyV1_1_30 {
Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
@@ -308,9 +307,9 @@ impl Default for ReplySurbsV1_1_20_2 {
}
}
impl From<ReplySurbsV1_1_20_2> for ReplySurbsV1_1_30 {
impl From<ReplySurbsV1_1_20_2> for ReplySurbs {
fn from(value: ReplySurbsV1_1_20_2) -> Self {
ReplySurbsV1_1_30 {
ReplySurbs {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -336,9 +335,9 @@ pub struct DebugConfigV1_1_20_2 {
pub reply_surbs: ReplySurbsV1_1_20_2,
}
impl From<DebugConfigV1_1_20_2> for DebugConfigV1_1_30 {
impl From<DebugConfigV1_1_20_2> for DebugConfig {
fn from(value: DebugConfigV1_1_20_2) -> Self {
DebugConfigV1_1_30 {
DebugConfig {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(),
@@ -1,472 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::topology_control::geo_aware_provider::CountryGroup;
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, GroupBy,
ReplySurbs, Topology, TopologyStructure, Traffic,
};
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_30 {
pub client: ClientV1_1_30,
#[serde(default)]
pub debug: DebugConfigV1_1_30,
}
impl From<ConfigV1_1_30> for Config {
fn from(value: ConfigV1_1_30) -> Self {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfig {
traffic: Traffic {
average_packet_delay: value.debug.traffic.average_packet_delay,
message_sending_average_delay: value
.debug
.traffic
.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.debug
.traffic
.disable_main_poisson_packet_distribution,
primary_packet_size: value.debug.traffic.primary_packet_size,
secondary_packet_size: value.debug.traffic.secondary_packet_size,
packet_type: value.debug.traffic.packet_type,
},
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value
.debug
.cover_traffic
.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value
.debug
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnection {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: Topology {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
max_startup_gateway_waiting_period: value
.debug
.topology
.max_startup_gateway_waiting_period,
topology_structure: value.debug.topology.topology_structure.into(),
},
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value
.debug
.reply_surbs
.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.debug.reply_surbs.maximum_reply_surb_age,
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
},
},
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct ClientV1_1_30 {
/// Version of the client for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TrafficV1_1_30 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity.
/// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
}
impl Default for TrafficV1_1_30 {
fn default() -> Self {
TrafficV1_1_30 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV1_1_30 {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
/// Only applicable if `secondary_packet_size` is enabled.
pub cover_traffic_primary_size_ratio: f64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTrafficV1_1_30 {
fn default() -> Self {
CoverTrafficV1_1_30 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_30 {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnectionV1_1_30 {
fn default() -> Self {
GatewayConnectionV1_1_30 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_30 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for AcknowledgementsV1_1_30 {
fn default() -> Self {
AcknowledgementsV1_1_30 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_30 {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructureV1_1_30,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructureV1_1_30 {
#[default]
NymApi,
GeoAware(GroupByV1_1_30),
}
impl From<TopologyStructureV1_1_30> for TopologyStructure {
fn from(value: TopologyStructureV1_1_30) -> Self {
match value {
TopologyStructureV1_1_30::NymApi => TopologyStructure::NymApi,
TopologyStructureV1_1_30::GeoAware(group_by) => {
TopologyStructure::GeoAware(group_by.into())
}
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupByV1_1_30 {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl From<GroupByV1_1_30> for GroupBy {
fn from(value: GroupByV1_1_30) -> Self {
match value {
GroupByV1_1_30::CountryGroup(country) => GroupBy::CountryGroup(country),
GroupByV1_1_30::NymAddress(addr) => GroupBy::NymAddress(addr),
}
}
}
impl std::fmt::Display for GroupByV1_1_30 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupByV1_1_30::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV1_1_30::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for TopologyV1_1_30 {
fn default() -> Self {
TopologyV1_1_30 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructureV1_1_30::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV1_1_30 {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
pub maximum_reply_surb_storage_threshold: usize,
/// Defines the minimum number of reply surbs the client would request.
pub minimum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs the client would request.
pub maximum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
pub maximum_allowed_reply_surb_request_size: u32,
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
/// for more even though in theory they wouldn't need to.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
/// Defines maximum amount of time the client is going to wait for reply surbs before
/// deciding it's never going to get them and would drop all pending messages
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
/// Defines maximum amount of time given reply surb is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
/// Defines maximum amount of time given reply key is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbsV1_1_30 {
fn default() -> Self {
ReplySurbsV1_1_30 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
}
}
}
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1_1_30 {
/// Defines all configuration options related to traffic streams.
pub traffic: TrafficV1_1_30,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficV1_1_30,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionV1_1_30,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsV1_1_30,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyV1_1_30,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV1_1_30,
}
-6
View File
@@ -126,12 +126,6 @@ pub enum ClientCoreError {
#[error("this client has performed gateway initialisation in another session")]
NoInitClientPresent,
#[error("there are no gateways supporting the wss protocol available")]
NoWssGateways,
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
UnsupportedWssProtocol { gateway: String },
}
/// Set of messages that the client can send to listeners via the task manager
+6 -38
View File
@@ -67,7 +67,7 @@ pub async fn current_gateways<R: Rng>(
log::trace!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_cached_described_gateways().await?;
let gateways = client.get_cached_gateways().await?;
let valid_gateways = gateways
.into_iter()
.filter_map(|gateway| gateway.try_into().ok())
@@ -174,10 +174,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
pub async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
let gateways = filter_by_tls(gateways, must_use_tls)?;
info!(
"choosing gateway by latency, pinging {} gateways ...",
gateways.len()
@@ -213,57 +210,28 @@ pub async fn choose_gateway_by_latency<R: Rng>(
Ok(chosen.gateway.clone())
}
fn filter_by_tls(
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<Vec<&gateway::Node>, ClientCoreError> {
if must_use_tls {
let filtered = gateways
.iter()
.filter(|g| g.clients_wss_port.is_some())
.collect::<Vec<_>>();
if filtered.is_empty() {
return Err(ClientCoreError::NoWssGateways);
}
Ok(filtered)
} else {
Ok(gateways.iter().collect())
}
}
pub(super) fn uniformly_random_gateway<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
filter_by_tls(gateways, must_use_tls)?
gateways
.choose(rng)
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
.map(|&r| r.clone())
.cloned()
}
pub(super) fn get_specified_gateway(
gateway_identity: IdentityKeyRef,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let gateway = gateways
gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
if must_use_tls && gateway.clients_wss_port.is_none() {
return Err(ClientCoreError::UnsupportedWssProtocol {
gateway: gateway_identity.to_string(),
});
}
Ok(gateway.clone())
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
}
pub(super) async fn register_with_gateway(
+6 -7
View File
@@ -108,20 +108,19 @@ where
let gateway_details = match selection_specification {
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
}
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
let gateway =
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
}
GatewaySelectionSpecification::Specified {
must_use_tls,
identity,
} => {
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
let gateway = get_specified_gateway(&identity, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
}
GatewaySelectionSpecification::Custom {
gateway_identity,
@@ -45,9 +45,6 @@ features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
workspace = true
# the choice of this particular tls feature was arbitrary;
# if you reckon a different one would be more appropriate, feel free to change it
features = ["native-tls"]
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
@@ -74,7 +74,7 @@ impl PartiallyDelegated {
fn route_socket_messages(
ws_msgs: Vec<Message>,
packet_router: &PacketRouter,
packet_router: &mut PacketRouter,
shared_key: &SharedKeys,
) -> Result<(), GatewayClientError> {
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key);
@@ -97,6 +97,7 @@ impl PartiallyDelegated {
let mixnet_receiver_future = async move {
let mut notify_receiver = notify_receiver;
let mut chunk_stream = (&mut stream).ready_chunks(8);
let mut packet_router = packet_router;
let ret_err = loop {
tokio::select! {
@@ -114,7 +115,7 @@ impl PartiallyDelegated {
Ok(msgs) => msgs
};
if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) {
if let Err(err) = Self::route_socket_messages(ws_msgs, &mut packet_router, shared_key.as_ref()) {
log::warn!("Route socket messages failed: {err}");
}
}
@@ -42,9 +42,7 @@ pub trait GatewayPacketRouter {
}
n if n
== PacketSize::OutfoxRegularPacket
.plaintext_size()
.saturating_sub(outfox_ack_overhead) =>
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead =>
{
trace!("received regular outfox packet");
received_messages.push(received_packet);
@@ -24,7 +24,6 @@ nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
http-api-client = { path = "../../../common/http-api-client"}
thiserror = { workspace = true }
log = { workspace = true }
url = { workspace = true, features = ["serde"] }
@@ -9,7 +9,7 @@ use nym_validator_client::nyxd::contract_traits::{
#[tokio::main]
async fn main() {
setup_env(Some("../../../envs/qa.env"));
setup_env(Some("../../../envs/qa-qwerty.env"));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
@@ -9,7 +9,7 @@ use nym_validator_client::nyxd::contract_traits::{
#[tokio::main]
async fn main() {
setup_env(Some("../../../envs/qa.env"));
setup_env(Some("../../../envs/qa-qwerty.env"));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
@@ -11,7 +11,7 @@ use crate::{
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::models::MixNodeBondAnnotated;
use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
@@ -19,7 +19,6 @@ use nym_api_requests::models::{
use nym_network_defaults::NymNetworkDetails;
use url::Url;
pub use crate::nym_api::NymApiClientExt;
pub use nym_mixnet_contract_common::{
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId,
};
@@ -148,7 +147,7 @@ impl Client<ReqwestRpcClient> {
impl<C> Client<C> {
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
let nym_api_client = nym_api::Client::new(config.api_url.clone());
Client {
nym_api: nym_api_client,
@@ -162,7 +161,7 @@ impl<C, S> Client<C, S> {
where
S: OfflineSigner,
{
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
let nym_api_client = nym_api::Client::new(config.api_url.clone());
Client {
nym_api: nym_api_client,
@@ -178,7 +177,7 @@ impl<C, S> Client<C, S> {
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_base_url(new_endpoint)
self.nym_api.change_url(new_endpoint)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
@@ -242,7 +241,7 @@ pub struct NymApiClient {
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url, None);
let nym_api = nym_api::Client::new(api_url);
NymApiClient { nym_api }
}
@@ -252,7 +251,7 @@ impl NymApiClient {
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_base_url(new_endpoint);
self.nym_api.change_url(new_endpoint);
}
pub async fn get_cached_active_mixnodes(
@@ -275,12 +274,6 @@ impl NymApiClient {
Ok(self.nym_api.get_gateways().await?)
}
pub async fn get_cached_described_gateways(
&self,
) -> Result<Vec<DescribedGateway>, ValidatorClientError> {
Ok(self.nym_api.get_gateways_described().await?)
}
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
@@ -1,7 +1,20 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use http_api_client::HttpClientError;
use nym_api_requests::models::RequestError;
use thiserror::Error;
pub type NymAPIError = HttpClientError<RequestError>;
#[derive(Error, Debug)]
pub enum NymAPIError {
#[error("There was an issue with the REST request - {source}")]
ReqwestClientError {
#[from]
source: reqwest::Error,
},
#[error("Not found")]
NotFound,
#[error("Request failed with error message - {0}")]
GenericRequestFailure(String),
#[error("The nym API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())]
ApiRequestFailure { status: u16, error: RequestError },
}
@@ -3,38 +3,140 @@
use crate::nym_api::error::NymAPIError;
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use async_trait::async_trait;
use http_api_client::{ApiClient, NO_PARAMS};
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use nym_api_requests::models::{
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
};
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_name_service_common::response::NamesListResponse;
use nym_service_provider_directory_common::response::ServicesListResponse;
use reqwest::{Response, StatusCode};
use serde::{Deserialize, Serialize};
use url::Url;
pub mod error;
pub mod routes;
pub use http_api_client::Client;
type PathSegments<'a> = &'a [&'a str];
type Params<'a, K, V> = &'a [(K, V)];
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait NymApiClientExt: ApiClient {
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
#[derive(Clone)]
pub struct Client {
url: Url,
reqwest_client: reqwest::Client,
}
impl Client {
pub fn new(url: Url) -> Self {
let reqwest_client = reqwest::Client::new();
Self {
url,
reqwest_client,
}
}
pub fn change_url(&mut self, new_url: Url) {
self.url = new_url
}
pub fn current_url(&self) -> &Url {
&self.url
}
async fn send_get_request<K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, NymAPIError>
where
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path, params);
Ok(self.reqwest_client.get(url).send().await?)
}
async fn query_nym_api<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, NymAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let res = self.send_get_request(path, params).await?;
if res.status().is_success() {
Ok(res.json().await?)
} else if res.status() == StatusCode::NOT_FOUND {
Err(NymAPIError::NotFound)
} else {
Err(NymAPIError::GenericRequestFailure(res.text().await?))
}
}
// This works for endpoints returning Result<Json<T>, ErrorResponse>
async fn query_nym_api_fallible<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, NymAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let res = self.send_get_request(path, params).await?;
let status = res.status();
if res.status().is_success() {
Ok(res.json().await?)
} else {
let request_error: RequestError = res.json().await?;
Err(NymAPIError::ApiRequestFailure {
status: status.as_u16(),
error: request_error,
})
}
}
async fn post_nym_api<B, T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, NymAPIError>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path, params);
let response = self.reqwest_client.post(url).json(json_body).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
Err(NymAPIError::GenericRequestFailure(response.text().await?))
}
}
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
.await
}
async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
pub async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -46,8 +148,8 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.get_json(
pub async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -59,10 +161,10 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_mixnodes_detailed_unfiltered(
pub async fn get_mixnodes_detailed_unfiltered(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -74,29 +176,23 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
self.get_json(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
.await
}
async fn get_gateways_described(&self) -> Result<Vec<DescribedGateway>, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED],
NO_PARAMS,
)
.await
}
async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
NO_PARAMS,
)
.await
}
async fn get_active_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
pub async fn get_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -109,19 +205,19 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
NO_PARAMS,
)
.await
}
async fn get_mixnode_report(
pub async fn get_mixnode_report(
&self,
mix_id: MixId,
) -> Result<MixnodeStatusReportResponse, NymAPIError> {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -134,11 +230,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_gateway_report(
pub async fn get_gateway_report(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayStatusReportResponse, NymAPIError> {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -151,11 +247,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_mixnode_history(
pub async fn get_mixnode_history(
&self,
mix_id: MixId,
) -> Result<MixnodeUptimeHistoryResponse, NymAPIError> {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -168,11 +264,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_gateway_history(
pub async fn get_gateway_history(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayUptimeHistoryResponse, NymAPIError> {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -185,10 +281,10 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_rewarded_mixnodes_detailed(
pub async fn get_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -201,13 +297,13 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_gateway_core_status_count(
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<GatewayCoreStatusResponse, NymAPIError> {
if let Some(since) = since {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -219,7 +315,7 @@ pub trait NymApiClientExt: ApiClient {
)
.await
} else {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -232,13 +328,13 @@ pub trait NymApiClientExt: ApiClient {
}
}
async fn get_mixnode_core_status_count(
pub async fn get_mixnode_core_status_count(
&self,
mix_id: MixId,
since: Option<i64>,
) -> Result<MixnodeCoreStatusResponse, NymAPIError> {
if let Some(since) = since {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -250,7 +346,7 @@ pub trait NymApiClientExt: ApiClient {
)
.await
} else {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -264,11 +360,11 @@ pub trait NymApiClientExt: ApiClient {
}
}
async fn get_mixnode_status(
pub async fn get_mixnode_status(
&self,
mix_id: MixId,
) -> Result<MixnodeStatusResponse, NymAPIError> {
self.get_json(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -281,11 +377,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_mixnode_reward_estimation(
pub async fn get_mixnode_reward_estimation(
&self,
mix_id: MixId,
) -> Result<RewardEstimationResponse, NymAPIError> {
self.get_json(
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -298,12 +394,12 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn compute_mixnode_reward_estimation(
pub async fn compute_mixnode_reward_estimation(
&self,
mix_id: MixId,
request_body: &ComputeRewardEstParam,
) -> Result<RewardEstimationResponse, NymAPIError> {
self.post_json(
self.post_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -317,11 +413,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_mixnode_stake_saturation(
pub async fn get_mixnode_stake_saturation(
&self,
mix_id: MixId,
) -> Result<StakeSaturationResponse, NymAPIError> {
self.get_json(
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -334,11 +430,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_mixnode_inclusion_probability(
pub async fn get_mixnode_inclusion_probability(
&self,
mix_id: MixId,
) -> Result<InclusionProbabilityResponse, NymAPIError> {
self.get_json(
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -351,8 +447,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_mixnode_avg_uptime(&self, mix_id: MixId) -> Result<UptimeResponse, NymAPIError> {
self.get_json(
pub async fn get_mixnode_avg_uptime(
&self,
mix_id: MixId,
) -> Result<UptimeResponse, NymAPIError> {
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -365,11 +464,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn blind_sign(
pub async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
) -> Result<BlindedSignatureResponse, NymAPIError> {
self.post_json(
self.post_nym_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
@@ -382,11 +481,11 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn verify_bandwidth_credential(
pub async fn verify_bandwidth_credential(
&self,
request_body: &VerifyCredentialBody,
) -> Result<VerifyCredentialResponse, NymAPIError> {
self.post_json(
self.post_nym_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
@@ -399,20 +498,118 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
pub async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
log::trace!("Getting service providers");
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
self.query_nym_api(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
.await
}
//async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
//pub async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
pub async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
log::trace!("Getting registered names");
self.get_json(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
self.query_nym_api(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl NymApiClientExt for Client {}
// utility function that should solve the double slash problem in validator API forever.
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
base: &Url,
segments: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Url {
let mut url = base.clone();
let mut path_segments = url
.path_segments_mut()
.expect("provided validator url does not have a base!");
for segment in segments {
let segment = segment.strip_prefix('/').unwrap_or(segment);
let segment = segment.strip_suffix('/').unwrap_or(segment);
path_segments.push(segment);
}
// I don't understand why compiler couldn't figure out that it's no longer used
// and can be dropped
drop(path_segments);
if !params.is_empty() {
url.query_pairs_mut().extend_pairs(params);
}
url
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn creating_api_path() {
let base_url: Url = "http://foomp.com".parse().unwrap();
// works with 1 segment
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["foo"], NO_PARAMS).as_str()
);
// works with 2 segments
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
);
// works with leading slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
);
// works with trailing slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
);
// works with both leading and trailing slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
);
// adds params
assert_eq!(
"http://foomp.com/foo/bar?foomp=baz",
create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
create_api_url(
&base_url,
&["/foo/", "/bar/"],
&[("arg1", "val1"), ("arg2", "val2")]
)
.as_str()
);
}
}
@@ -6,7 +6,6 @@ use nym_network_defaults::NYM_API_VERSION;
pub const API_VERSION: &str = NYM_API_VERSION;
pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways";
pub const DESCRIBED: &str = "described";
pub const DETAILED: &str = "detailed";
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
@@ -8,7 +8,6 @@ use crate::nyxd::cosmwasm_client::types::{
Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse,
};
use crate::nyxd::error::NyxdError;
use crate::nyxd::Query;
use crate::rpc::TendermintRpcClient;
use async_trait::async_trait;
use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry};
@@ -36,6 +35,7 @@ use std::convert::TryFrom;
use std::time::Duration;
use tendermint_rpc::{
endpoint::{block::Response as BlockResponse, broadcast, tx::Response as TxResponse},
query::Query,
Order,
};
@@ -18,7 +18,7 @@ use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, Reqwes
use async_trait::async_trait;
use cosmrs::cosmwasm;
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
use cosmrs::tx::{Raw, SignDoc};
use cosmrs::tx::{Msg, Raw, SignDoc};
use cosmwasm_std::Addr;
use nym_network_defaults::{ChainDetails, NymNetworkDetails};
use serde::{de::DeserializeOwned, Serialize};
@@ -39,7 +39,6 @@ pub use cosmrs::tendermint::block::Height;
pub use cosmrs::tendermint::hash::{self, Algorithm, Hash};
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::Msg;
pub use cosmrs::tx::{self};
pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::Gas;
@@ -48,7 +47,6 @@ pub use cosmwasm_std::Coin as CosmWasmCoin;
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
pub use tendermint_rpc::{
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
query::Query,
Paging,
};
pub use tendermint_rpc::{Request, Response, SimpleRequest};
@@ -59,6 +57,7 @@ use crate::http_client;
use crate::{DirectSigningHttpRpcNyxdClient, QueryHttpRpcNyxdClient};
#[cfg(feature = "http-client")]
use cosmrs::rpc::{HttpClient, HttpClientUrl};
use tendermint_rpc::query::Query;
pub mod coin;
pub mod contract_traits;
+1 -1
View File
@@ -8,6 +8,6 @@ description = "Crutch library until there is proper SerDe support for coconut st
bs58 = "0.4.0"
getset = "0.1.1"
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
thiserror = "1"
nym-coconut = {path = "../nymcoconut" }
@@ -60,7 +60,7 @@ pub async fn init(args: Args, client: SigningClient, network_details: &NymNetwor
// by default we make ourselves an admin, let me know if you don't like that behaviour
let opts = Some(InstantiateOptions {
funds,
admin: Some(args.admin.unwrap_or_else(|| client.address())),
admin: Some(args.admin.unwrap_or_else(|| client.address().clone())),
});
let msg: serde_json::Value =
@@ -1,11 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_cosmwasm_coin, show_error};
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_cosmwasm_coin, show_error};
#[derive(Debug, Parser)]
pub struct Args {
@@ -1,11 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_decimal_with_denom, show_error};
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_decimal_with_denom, show_error};
#[derive(Debug, Parser)]
pub struct Args {
@@ -1,13 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::nym_api::error::NymAPIError;
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
@@ -1,13 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::nym_api::error::NymAPIError;
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
-1
View File
@@ -15,7 +15,6 @@ pub use toml::de::Error as TomlDeError;
pub mod defaults;
pub mod helpers;
pub mod legacy_helpers;
pub mod serde_helpers;
pub const NYM_DIR: &str = ".nym";
pub const DEFAULT_NYM_APIS_DIR: &str = "nym-api";
-47
View File
@@ -1,47 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Deserializer};
use std::fmt::Display;
use std::path::PathBuf;
use std::str::FromStr;
pub fn de_maybe_stringified<'de, D, T, E>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: FromStr<Err = E>,
E: Display,
{
let raw = String::deserialize(deserializer)?;
if raw.is_empty() {
Ok(None)
} else {
Ok(Some(raw.parse().map_err(serde::de::Error::custom)?))
}
}
pub fn de_maybe_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
de_maybe_stringified(deserializer)
}
pub fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
D: Deserializer<'de>,
{
de_maybe_stringified(deserializer)
}
pub 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))
}
}
@@ -18,7 +18,7 @@ serde_repr = "0.1"
# we still have to preserve that import for `JsonSchema` for `Layer` type (since we can't use cw_serde macro due to custom serde impl)
schemars = "0.8"
thiserror = { workspace = true }
thiserror = "1.0"
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
serde-json-wasm = { workspace = true }
humantime-serde = "1.1.1"
@@ -14,4 +14,4 @@ cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { workspace = true }
thiserror = { version = "1.0.23" }
@@ -14,7 +14,7 @@ cw2 = { workspace = true, optional = true }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
serde = { version = "1.0", features = ["derive"] }
thiserror = { workspace = true }
thiserror = "1.0"
ts-rs = { workspace = true, optional = true}
[features]
+1 -1
View File
@@ -9,7 +9,7 @@ edition = "2021"
async-trait = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
thiserror = "1.0"
tokio = { version = "1.24.1", features = ["sync"]}
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
+1 -1
View File
@@ -23,7 +23,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
serde_bytes = { version = "0.11.6", optional = true }
serde_crate = { version = "1.0", optional = true, default_features = false, features = ["derive"], package = "serde" }
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
thiserror = { workspace = true }
thiserror = "1.0.37"
zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
# internal
+5 -9
View File
@@ -145,12 +145,8 @@ impl PublicKey {
Self::from_bytes(&bytes)
}
pub fn verify<M: AsRef<[u8]>>(
&self,
message: M,
signature: &Signature,
) -> Result<(), SignatureError> {
self.0.verify(message.as_ref(), &signature.0)
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
self.0.verify(message, &signature.0)
}
}
@@ -243,16 +239,16 @@ impl PrivateKey {
Self::from_bytes(&bytes)
}
pub fn sign<M: AsRef<[u8]>>(&self, message: M) -> Signature {
pub fn sign(&self, message: &[u8]) -> Signature {
let expanded_secret_key = ed25519_dalek::ExpandedSecretKey::from(&self.0);
let public_key: PublicKey = self.into();
let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0);
let sig = expanded_secret_key.sign(message, &public_key.0);
Signature(sig)
}
/// Signs text with the provided Ed25519 private key, returning a base58 signature
pub fn sign_text(&self, text: &str) -> String {
let signature_bytes = self.sign(text).to_bytes();
let signature_bytes = self.sign(text.as_ref()).to_bytes();
bs58::encode(signature_bytes).into_string()
}
}
+3 -3
View File
@@ -21,10 +21,10 @@ rand = { version = "0.8.5", default-features = false}
rand_chacha = "0.3"
rand_core = "0.6.3"
sha2 = "0.9"
serde = { workspace = true }
serde = "1.0"
serde_derive = "1.0"
thiserror = { workspace = true }
zeroize = { workspace = true, features = ["zeroize_derive"] }
thiserror = "1.0"
zeroize = { version = "1.4", features = ["zeroize_derive"] }
nym-pemstore = { path = "../pemstore" }
-33
View File
@@ -1,33 +0,0 @@
[package]
name = "nym-exit-policy"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
tracing = { workspace = true }
# feature-specific dependencies:
## client feature
reqwest = { workspace = true, optional = true }
## openapi feature
serde_json = { workspace = true, optional = true }
utoipa = { workspace = true, optional = true }
[dev-dependencies]
serde_json = { workspace = true }
[features]
default = []
client = ["reqwest"]
openapi = ["utoipa", "serde_json"]
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::policy::PolicyError;
use crate::ExitPolicy;
use reqwest::IntoUrl;
pub async fn get_exit_policy(url: impl IntoUrl) -> Result<ExitPolicy, PolicyError> {
ExitPolicy::parse_from_torrc(reqwest::get(url).await?.text().await?)
}
-233
View File
@@ -1,233 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod policy;
#[cfg(feature = "client")]
pub mod client;
pub use crate::policy::{
AddressPolicy, AddressPolicyAction, AddressPolicyRule, AddressPortPattern, PolicyError,
PortRange,
};
pub(crate) const EXIT_POLICY_FIELD_NAME: &str = "ExitPolicy";
const COMMENT_CHAR: char = '#';
pub type ExitPolicy = AddressPolicy;
pub fn parse_exit_policy<S: AsRef<str>>(exit_policy: S) -> Result<ExitPolicy, PolicyError> {
let rules = exit_policy
.as_ref()
.lines()
.map(|maybe_rule| {
if let Some(comment_start) = maybe_rule.find(COMMENT_CHAR) {
&maybe_rule[..comment_start]
} else {
maybe_rule
}
.trim()
})
.filter(|maybe_rule| !maybe_rule.is_empty())
.map(parse_address_policy_rule)
.collect::<Result<Vec<_>, _>>()?;
Ok(AddressPolicy { rules })
}
pub fn format_exit_policy(policy: &ExitPolicy) -> String {
policy
.rules
.iter()
.map(|rule| format!("{EXIT_POLICY_FIELD_NAME} {rule}"))
.fold(String::new(), |accumulator, rule| {
accumulator + &rule + "\n"
})
.trim_end()
.to_string()
}
fn parse_address_policy_rule(rule: &str) -> Result<AddressPolicyRule, PolicyError> {
// each exit policy rule must begin with 'ExitPolicy' followed by the actual rule
rule.strip_prefix(EXIT_POLICY_FIELD_NAME)
.ok_or(PolicyError::NoExitPolicyPrefix {
entry: rule.to_string(),
})?
.trim()
.parse()
}
// for each line, ignore everything after the comment
#[cfg(test)]
mod tests {
use super::*;
use crate::policy::AddressPolicyAction::{Accept, Accept6, Reject, Reject6};
use crate::policy::{AddressPortPattern, IpPattern, PortRange};
#[test]
fn parsing_policy() {
let sample = r#"
ExitPolicy reject 1.2.3.4/32:*#comment
ExitPolicy reject 1.2.3.5:* #comment
ExitPolicy reject 1.2.3.6/16:*
ExitPolicy reject 1.2.3.6/16:123-456 # comment
ExitPolicy accept *:53 # DNS
# random comment
ExitPolicy accept6 *6:119
ExitPolicy accept *4:120
ExitPolicy reject6 [FC00::]/7:*
#ExitPolicy accept *:8080 #and another comment here
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328:1234
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328/64:1235
#another comment
#ExitPolicy accept *:8080
ExitPolicy reject *:*
"#;
let res = parse_exit_policy(sample).unwrap();
let mut expected = AddressPolicy::new();
// ExitPolicy reject 1.2.3.4/32:*#comment
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V4 {
addr_prefix: "1.2.3.4".parse().unwrap(),
mask: 32,
},
ports: PortRange::new_all(),
},
);
// ExitPolicy reject 1.2.3.5:* #comment
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V4 {
addr_prefix: "1.2.3.5".parse().unwrap(),
mask: 32,
},
ports: PortRange::new_all(),
},
);
// ExitPolicy reject 1.2.3.6/16:*
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V4 {
addr_prefix: "1.2.3.6".parse().unwrap(),
mask: 16,
},
ports: PortRange::new_all(),
},
);
// ExitPolicy reject 1.2.3.6/16:123-456
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V4 {
addr_prefix: "1.2.3.6".parse().unwrap(),
mask: 16,
},
ports: PortRange::new(123, 456).unwrap(),
},
);
// ExitPolicy accept *:53
expected.push(
Accept,
AddressPortPattern {
ip_pattern: IpPattern::Star,
ports: PortRange::new_singleton(53),
},
);
// ExitPolicy accept6 *6:119
expected.push(
Accept6,
AddressPortPattern {
ip_pattern: IpPattern::V6Star,
ports: PortRange::new_singleton(119),
},
);
// ExitPolicy accept *4:120
expected.push(
Accept,
AddressPortPattern {
ip_pattern: IpPattern::V4Star,
ports: PortRange::new_singleton(120),
},
);
// ExitPolicy reject6 [FC00::]/7:*
expected.push(
Reject6,
AddressPortPattern {
ip_pattern: IpPattern::V6 {
addr_prefix: "FC00::".parse().unwrap(),
mask: 7,
},
ports: PortRange::new_all(),
},
);
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V6 {
addr_prefix: "FE80:0000:0000:0000:0202:B3FF:FE1E:8329".parse().unwrap(),
mask: 128,
},
ports: PortRange::new_all(),
},
);
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8328:1234
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V6 {
addr_prefix: "FE80:0000:0000:0000:0202:B3FF:FE1E:8328".parse().unwrap(),
mask: 128,
},
ports: PortRange::new_singleton(1234),
},
);
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8328/64:1235
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V6 {
addr_prefix: "FE80:0000:0000:0000:0202:B3FF:FE1E:8328".parse().unwrap(),
mask: 64,
},
ports: PortRange::new_singleton(1235),
},
);
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::Star,
ports: PortRange::new_all(),
},
);
assert_eq!(res, expected)
}
}
@@ -1,726 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Implements address policies, based on a series of accept/reject
//! rules.
use crate::policy::error::PolicyError;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::str::FromStr;
use tracing::trace;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "lowercase")]
pub enum AddressPolicyAction {
/// A rule that accepts matching address:port combinations on IPv4 and IPv6.
Accept,
/// A rule that rejects matching address:port combinations on IPv4 and IPv6.
Reject,
/// A rule that accepts matching address:port combinations on IPv6 only.
Accept6,
/// A rule that rejects matching address:port combinations on IPv6 only.
Reject6,
}
impl AddressPolicyAction {
pub fn is_accept(&self) -> bool {
matches!(
self,
AddressPolicyAction::Accept | AddressPolicyAction::Accept6
)
}
}
impl FromStr for AddressPolicyAction {
type Err = PolicyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"accept" => Ok(AddressPolicyAction::Accept),
"reject" => Ok(AddressPolicyAction::Reject),
"accept6" => Ok(AddressPolicyAction::Accept6),
"reject6" => Ok(AddressPolicyAction::Reject6),
other => Err(PolicyError::InvalidPolicyAction {
action: other.to_string(),
}),
}
}
}
impl Display for AddressPolicyAction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AddressPolicyAction::Accept => write!(f, "accept"),
AddressPolicyAction::Reject => write!(f, "reject"),
AddressPolicyAction::Accept6 => write!(f, "accept6"),
AddressPolicyAction::Reject6 => write!(f, "reject6"),
}
}
}
/// A sequence of rules that are applied to an address:port until one
/// matches.
///
/// Each rule is of the form "accept(6) PATTERN" or "reject(6) PATTERN",
/// where every pattern describes a set of addresses and ports.
/// Address sets are given as a prefix of 0-128 bits that the address
/// must have; port sets are given as a low-bound and high-bound that
/// the target port might lie between.
///
/// An example IPv4 policy might be:
///
/// ```text
/// reject *:25
/// reject 127.0.0.0/8:*
/// reject 192.168.0.0/16:*
/// accept *:80
/// accept *:443
/// accept *:9000-65535
/// reject *:*
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "openapi", aliases(ExitPolicy))]
pub struct AddressPolicy {
/// A list of rules to apply to find out whether an address is
/// contained by this policy.
///
/// The rules apply in order; the first one to match determines
/// whether the address is accepted or rejected.
pub(crate) rules: Vec<AddressPolicyRule>,
}
impl AddressPolicy {
/// Create a new AddressPolicy that matches nothing.
pub const fn new() -> Self {
AddressPolicy { rules: Vec::new() }
}
/// Create a new AddressPolicy that matches everything.
pub fn new_open() -> Self {
AddressPolicy {
rules: vec![AddressPolicyRule::new(
AddressPolicyAction::Accept,
AddressPortPattern {
ip_pattern: IpPattern::Star,
ports: PortRange::new_all(),
},
)],
}
}
/// Check whether this AddressPolicy matches all patterns.
pub fn is_open(&self) -> bool {
if self.rules.len() != 1 {
return false;
}
let rule = &self.rules[0];
rule.action == AddressPolicyAction::Accept
&& rule.pattern.ip_pattern == IpPattern::Star
&& rule.pattern.ports.is_all()
}
/// Attempts to parse the AddressPolicy out of raw torrc representation.
pub fn parse_from_torrc<S: AsRef<str>>(raw: S) -> Result<Self, PolicyError> {
crate::parse_exit_policy(raw)
}
/// Formats the AddressPolicy with torrc representation
pub fn format_as_torrc(&self) -> String {
crate::format_exit_policy(self)
}
/// Apply this policy to an address:port combination
///
/// We do this by applying each rule in sequence, until one
/// matches.
///
/// Returns None if no rule matches.
pub fn allows(&self, addr: &IpAddr, port: u16) -> Option<bool> {
self.rules
.iter()
.find(|rule| rule.pattern.matches(addr, port))
.map(|rule| {
trace!("'{addr}:{port}' is covered by rule '{rule}'");
rule.action.is_accept()
})
}
/// As allows, but accept a SocketAddr.
pub fn allows_sockaddr(&self, addr: &SocketAddr) -> Option<bool> {
self.allows(&addr.ip(), addr.port())
}
/// Add a new rule to this policy.
///
/// The newly added rule is applied _after_ all previous rules.
/// It matches all addresses and ports covered by AddressPortPattern.
///
/// If accept is true, the rule is to accept addresses that match;
/// if accept is false, the rule rejects such addresses.
pub fn push(&mut self, action: AddressPolicyAction, pattern: AddressPortPattern) {
self.rules.push(AddressPolicyRule { action, pattern })
}
/// As push, but accepts a AddressPolicyRule.
pub fn push_rule(&mut self, rule: AddressPolicyRule) {
self.rules.push(rule)
}
}
/// A single rule in an address policy.
///
/// Contains a pattern, what to do with things that match it.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AddressPolicyRule {
/// What do we do with items that match the pattern?
action: AddressPolicyAction,
/// What pattern are we trying to match?
pattern: AddressPortPattern,
}
impl FromStr for AddressPolicyRule {
type Err = PolicyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// split on the first space, i.e. separation between the action and the pattern
let Some((action, pattern)) = s.split_once(' ') else {
return Err(PolicyError::MalformedAddressPolicy { raw: s.to_string() });
};
Ok(AddressPolicyRule {
action: action.parse()?,
pattern: pattern.parse()?,
})
}
}
impl AddressPolicyRule {
pub fn new(action: AddressPolicyAction, pattern: AddressPortPattern) -> Self {
AddressPolicyRule { action, pattern }
}
}
impl Display for AddressPolicyRule {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.action, self.pattern)
}
}
/// A pattern that may or may not match an address and port.
///
/// Each AddrPortPattern has an IP pattern, which matches a set of
/// addresses by prefix, and a port pattern, which matches a range of
/// ports.
///
/// # Example
///
/// ```
/// use nym_exit_policy::policy::AddressPortPattern;
/// use std::net::{IpAddr,Ipv4Addr};
/// let localhost = IpAddr::V4(Ipv4Addr::new(127,3,4,5));
/// let not_localhost = IpAddr::V4(Ipv4Addr::new(192,0,2,16));
/// let pat: AddressPortPattern = "127.0.0.0/8:*".parse().unwrap();
///
/// assert!(pat.matches(&localhost, 22));
/// assert!(!pat.matches(&not_localhost, 22));
/// ```
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AddressPortPattern {
/// A pattern to match somewhere between zero and all IP addresses.
#[serde(with = "stringified_ip_pattern")]
#[cfg_attr(feature = "openapi", schema(example = "1.2.3.6/16", value_type = String))]
pub(crate) ip_pattern: IpPattern,
/// A pattern to match a range of ports.
pub(crate) ports: PortRange,
}
mod stringified_ip_pattern {
use super::IpPattern;
use serde::{Deserialize, Deserializer, Serializer};
use std::str::FromStr;
pub fn serialize<S: Serializer>(pattern: &IpPattern, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&pattern.to_string())
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<IpPattern, D::Error> {
let s = <String>::deserialize(deserializer)?;
IpPattern::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl AddressPortPattern {
/// Return true iff this pattern matches a given address and port.
pub fn matches(&self, addr: &IpAddr, port: u16) -> bool {
self.ip_pattern.matches(addr) && self.ports.contains(port)
}
/// As matches, but accept a SocketAddr.
pub fn matches_sockaddr(&self, addr: &SocketAddr) -> bool {
self.matches(&addr.ip(), addr.port())
}
}
impl Display for AddressPortPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.ip_pattern, self.ports)
}
}
impl FromStr for AddressPortPattern {
type Err = PolicyError;
fn from_str(s: &str) -> Result<Self, PolicyError> {
let last_colon = s
.rfind(':')
.ok_or(PolicyError::MalformedAddressPortPattern { raw: s.to_string() })?;
// doesn't have enough chars to cover the port, even if its a wildcard
if s.len() < last_colon + 2 {
return Err(PolicyError::MalformedAddressPortPattern { raw: s.to_string() });
}
let ip_pattern = s[..last_colon].parse()?;
let ports = s[last_colon + 1..].parse()?;
Ok(AddressPortPattern { ip_pattern, ports })
}
}
/// A pattern that matches one or more IP addresses.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IpPattern {
/// Match all addresses.
Star,
/// Match all IPv4 addresses.
V4Star,
/// Match all IPv6 addresses.
V6Star,
/// Match all IPv4 addresses beginning with a given prefix and mask.
V4 { addr_prefix: Ipv4Addr, mask: u8 },
/// Match all IPv6 addresses beginning with a given prefix and mask.
V6 { addr_prefix: Ipv6Addr, mask: u8 },
}
impl IpPattern {
/// Construct an IpPattern that matches the first `mask` bits of `addr`.
fn from_addr_and_mask(address: IpAddr, target_mask: u8) -> Result<Self, PolicyError> {
match (address, target_mask) {
(IpAddr::V4(_), 0) => Ok(IpPattern::V4Star),
(IpAddr::V6(_), 0) => Ok(IpPattern::V6Star),
(IpAddr::V4(addr_prefix), mask) if mask <= 32 => {
Ok(IpPattern::V4 { addr_prefix, mask })
}
(IpAddr::V6(addr_prefix), mask) if mask <= 128 => {
Ok(IpPattern::V6 { addr_prefix, mask })
}
(addr, mask) => {
if addr.is_ipv4() {
Err(PolicyError::InvalidIpV4Mask { mask })
} else {
Err(PolicyError::InvalidIpV6Mask { mask })
}
}
}
}
/// Return true iff `addr` is matched by this pattern.
fn matches(&self, addr: &IpAddr) -> bool {
match (self, addr) {
(IpPattern::Star, _) => true,
(IpPattern::V4Star, IpAddr::V4(_)) => true,
(IpPattern::V6Star, IpAddr::V6(_)) => true,
(IpPattern::V4 { addr_prefix, mask }, IpAddr::V4(addr)) => {
let p1 = u32::from_be_bytes(addr_prefix.octets());
let p2 = u32::from_be_bytes(addr.octets());
let shift = 32 - mask;
(p1 >> shift) == (p2 >> shift)
}
(IpPattern::V6 { addr_prefix, mask }, IpAddr::V6(addr)) => {
let p1 = u128::from_be_bytes(addr_prefix.octets());
let p2 = u128::from_be_bytes(addr.octets());
let shift = 128 - mask;
(p1 >> shift) == (p2 >> shift)
}
(_, _) => false,
}
}
}
impl Display for IpPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IpPattern::Star => write!(f, "*"),
IpPattern::V4Star => write!(f, "*4"),
IpPattern::V6Star => write!(f, "*6"),
IpPattern::V4 { addr_prefix, mask } => {
write!(f, "{addr_prefix}/{mask}")
}
IpPattern::V6 { addr_prefix, mask } => {
write!(f, "{addr_prefix}/{mask}")
}
}
}
}
/// Helper: try to parse a plain ipv4 address, or an IPv6 address
/// wrapped in brackets.
fn parse_addr(s: &str) -> Result<IpAddr, PolicyError> {
if s.starts_with('[') && s.ends_with(']') {
Ipv6Addr::from_str(&s[1..s.len() - 1]).map(IpAddr::V6)
} else {
IpAddr::from_str(s)
}
.map_err(|source| PolicyError::MalformedIpAddress {
addr: s.to_string(),
source,
})
}
/// Helper: try to parse a port making sure it's non-zero
fn parse_port(s: &str) -> Result<u16, PolicyError> {
let port = s
.parse::<u16>()
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })?;
if port == 0 {
Err(PolicyError::InvalidPort {
raw: port.to_string(),
})
} else {
Ok(port)
}
}
impl FromStr for IpPattern {
type Err = PolicyError;
fn from_str(s: &str) -> Result<Self, PolicyError> {
let (ip_s, mask_s) = match s.find('/') {
Some(slash_idx) => (&s[..slash_idx], Some(&s[slash_idx + 1..])),
None => (s, None),
};
match (ip_s, mask_s) {
// '*' patterns
("*", Some(m)) => Err(PolicyError::MaskWithStar {
mask: m.to_string(),
}),
("*", None) => Ok(IpPattern::Star),
// '*4' patterns
("*4", Some(m)) => Err(PolicyError::MaskWithV4Star {
mask: m.to_string(),
}),
("*4", None) => Ok(IpPattern::V4Star),
// '*6' patterns
("*6", Some(m)) => Err(PolicyError::MaskWithV6Star {
mask: m.to_string(),
}),
("*6", None) => Ok(IpPattern::V6Star),
(s, Some(m)) => {
let a: IpAddr = parse_addr(s)?;
let m: u8 = m.parse().map_err(|_| PolicyError::InvalidMask {
mask: m.to_string(),
})?;
IpPattern::from_addr_and_mask(a, m)
}
(s, None) => {
let a: IpAddr = parse_addr(s)?;
let m = if a.is_ipv4() { 32 } else { 128 };
IpPattern::from_addr_and_mask(a, m)
}
}
}
}
/// A PortRange is a set of consecutively numbered TCP or UDP ports.
///
/// # Example
/// ```
/// use nym_exit_policy::policy::PortRange;
///
/// let r: PortRange = "22-8000".parse().unwrap();
/// assert!(r.contains(128));
/// assert!(r.contains(22));
/// assert!(r.contains(8000));
///
/// assert!(! r.contains(21));
/// assert!(! r.contains(8001));
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct PortRange {
/// The first port in this range.
#[cfg_attr(feature = "openapi", schema(example = 80))]
pub start: u16,
/// The last port in this range.
#[cfg_attr(feature = "openapi", schema(example = 81))]
pub end: u16,
}
impl PortRange {
/// Create a new port range spanning from start to end, asserting that
/// the correct invariants hold.
fn new_unchecked(start: u16, end: u16) -> Self {
assert_ne!(start, 0);
assert!(start <= end);
PortRange { start, end }
}
/// Create a port range containing all ports.
pub fn new_all() -> Self {
PortRange::new_unchecked(1, 65535)
}
/// Create a new PortRange.
///
/// The Portrange contains all ports between `start` and `end` inclusive.
///
/// Returns None if lo is greater than end, or if either is zero.
pub const fn new(start: u16, end: u16) -> Option<Self> {
if start != 0 && start <= end {
Some(PortRange { start, end })
} else {
None
}
}
/// Create a new singleton PortRange.
pub const fn new_singleton(value: u16) -> Self {
PortRange {
start: value,
end: value,
}
}
/// Return true if a port is in this range.
pub fn contains(&self, port: u16) -> bool {
self.start <= port && port <= self.end
}
/// Return true if this range contains all ports.
pub fn is_all(&self) -> bool {
self.start == 1 && self.end == 65535
}
}
/// A PortRange is displayed as a number if it contains a single port,
/// and as a start point and end point separated by a dash if it contains
/// more than one port.
impl Display for PortRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_all() {
write!(f, "*")
} else if self.start == self.end {
write!(f, "{}", self.start)
} else {
write!(f, "{}-{}", self.start, self.end)
}
}
}
impl FromStr for PortRange {
type Err = PolicyError;
fn from_str(s: &str) -> Result<Self, PolicyError> {
// check is if it's a star range
if s == "*" {
return Ok(PortRange::new_all());
}
if let Some(pos) = s.find('-') {
// This is a range; parse each part
let start = parse_port(&s[..pos])?;
let end = parse_port(&s[pos + 1..])?;
PortRange::new(start, end).ok_or(PolicyError::InvalidRange { start, end })
} else {
// There was no hyphen, so try to parse this range as a singleton.
let value = parse_port(s)?;
Ok(PortRange::new_singleton(value))
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_bad_rules() {
fn check(s: &str) {
assert!(s.parse::<AddressPortPattern>().is_err());
}
check("marzipan:80");
check("1.2.3.4:90-80");
check("1.2.3.4/100:8888");
check("[1.2.3.4]/16:80");
check("[::1]/130:8888");
}
#[test]
fn test_rule_matches() {
fn check(address: &str, yes: &[&str], no: &[&str]) {
use std::net::SocketAddr;
let policy = address.parse::<AddressPortPattern>().unwrap();
for s in yes {
let sa = s.parse::<SocketAddr>().unwrap();
assert!(policy.matches_sockaddr(&sa));
}
for s in no {
let sa = s.parse::<SocketAddr>().unwrap();
assert!(!policy.matches_sockaddr(&sa));
}
}
check(
"1.2.3.4/16:80",
&["1.2.3.4:80", "1.2.44.55:80"],
&["9.9.9.9:80", "1.3.3.4:80", "1.2.3.4:81"],
);
check(
"*:443-8000",
&["1.2.3.4:443", "[::1]:500"],
&["9.0.0.0:80", "[::1]:80"],
);
check(
"[face::]/8:80",
&["[fab0::7]:80"],
&["[dd00::]:80", "[face::7]:443"],
);
check("0.0.0.0/0:*", &["127.0.0.1:80"], &["[f00b::]:80"]);
check("[::]/0:*", &["[f00b::]:80"], &["127.0.0.1:80"]);
}
#[test]
fn test_policy_matches() -> Result<(), PolicyError> {
let mut policy = AddressPolicy::default();
policy.push(AddressPolicyAction::Accept, "*:443".parse()?);
policy.push(AddressPolicyAction::Accept, "[::1]:80".parse()?);
policy.push(AddressPolicyAction::Reject, "*:80".parse()?);
let policy = policy; // drop mut
assert!(policy
.allows_sockaddr(&"[::6]:443".parse().unwrap())
.unwrap());
assert!(policy
.allows_sockaddr(&"127.0.0.1:443".parse().unwrap())
.unwrap());
assert!(policy
.allows_sockaddr(&"[::1]:80".parse().unwrap())
.unwrap());
assert!(!policy
.allows_sockaddr(&"[::2]:80".parse().unwrap())
.unwrap());
assert!(!policy
.allows_sockaddr(&"127.0.0.1:80".parse().unwrap())
.unwrap());
assert!(policy
.allows_sockaddr(&"127.0.0.1:66".parse().unwrap())
.is_none());
Ok(())
}
#[test]
fn parse_portrange() {
assert_eq!(
"1-100".parse::<PortRange>().unwrap(),
PortRange::new(1, 100).unwrap()
);
assert_eq!(
"01-100".parse::<PortRange>().unwrap(),
PortRange::new(1, 100).unwrap()
);
assert_eq!(
"1-65535".parse::<PortRange>().unwrap(),
PortRange::new_all()
);
assert_eq!(
"10-30".parse::<PortRange>().unwrap(),
PortRange::new(10, 30).unwrap()
);
assert_eq!(
"9001".parse::<PortRange>().unwrap(),
PortRange::new(9001, 9001).unwrap()
);
assert_eq!(
"9001-9001".parse::<PortRange>().unwrap(),
PortRange::new(9001, 9001).unwrap()
);
assert_eq!("*".parse::<PortRange>().unwrap(), PortRange::new_all());
assert!("hello".parse::<PortRange>().is_err());
assert!("0".parse::<PortRange>().is_err());
assert!("65536".parse::<PortRange>().is_err());
assert!("65537".parse::<PortRange>().is_err());
assert!("1-2-3".parse::<PortRange>().is_err());
assert!("10-5".parse::<PortRange>().is_err());
assert!("1-".parse::<PortRange>().is_err());
assert!("-2".parse::<PortRange>().is_err());
assert!("-".parse::<PortRange>().is_err());
}
#[test]
fn test_portrange() {
assert!(PortRange::new_all().is_all());
assert!(!PortRange::new(2, 65535).unwrap().is_all());
assert!(PortRange::new_all().contains(1));
assert!(PortRange::new_all().contains(65535));
assert!(PortRange::new_all().contains(7777));
assert!(PortRange::new(20, 30).unwrap().contains(20));
assert!(PortRange::new(20, 30).unwrap().contains(25));
assert!(PortRange::new(20, 30).unwrap().contains(30));
assert!(!PortRange::new(20, 30).unwrap().contains(19));
assert!(!PortRange::new(20, 30).unwrap().contains(31));
}
// this test exists due to manually implemented 'stringified_ip_pattern' on 'AddressPortPattern'
#[test]
fn policy_serde_json_roundtrip() {
let policy = AddressPolicy::parse_from_torrc(
r#"
ExitPolicy reject 1.2.3.4/32:*
ExitPolicy reject 1.2.3.5:*
ExitPolicy reject 1.2.3.6/16:*
ExitPolicy reject 1.2.3.6/16:123-456
ExitPolicy accept *:53
ExitPolicy accept6 *6:119
ExitPolicy accept *4:120
ExitPolicy reject6 [FC00::]/7:*
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328:1234
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328/64:1235
ExitPolicy reject *:*"#,
)
.unwrap();
let json = serde_json::to_string(&policy).unwrap();
let recovered: AddressPolicy = serde_json::from_str(&json).unwrap();
assert_eq!(recovered, policy);
}
}
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::EXIT_POLICY_FIELD_NAME;
use std::net::AddrParseError;
use thiserror::Error;
/// Error from an unparsable or invalid policy.
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PolicyError {
#[cfg(feature = "client")]
#[error("failed to fetch the remote policy: {source}")]
ClientError {
#[from]
source: reqwest::Error,
},
#[error("/{mask} is not a valid mask for an IpV4 address")]
InvalidIpV4Mask { mask: u8 },
#[error("/{mask} is not a valid mask for an IpV6 address")]
InvalidIpV6Mask { mask: u8 },
#[error("'{action}' is not a valid policy action")]
InvalidPolicyAction { action: String },
#[error("'{addr}' is not a valid Ip address: {source}")]
MalformedIpAddress {
addr: String,
#[source]
source: AddrParseError,
},
/// Attempted to use a bitmask with the address "*".
#[error("attempted to use a bitmask ('/{mask}') with the address '*'")]
MaskWithStar { mask: String },
/// Attempted to use a bitmask with the address "*4".
#[error("attempted to use a bitmask ('/{mask}') with the address '*4'")]
MaskWithV4Star { mask: String },
/// Attempted to use a bitmask with the address "*6".
#[error("attempted to use a bitmask ('/{mask}') with the address '*6'")]
MaskWithV6Star { mask: String },
#[error("'/{mask}' is not a valid mask")]
InvalidMask { mask: String },
/// A port was not a number in the range 1..65535
#[error(
"the provided port '{raw}' was either malformed or was not in the valid 1..65535 range"
)]
InvalidPort { raw: String },
/// A port range had its starting-point higher than its ending point.
#[error("the provided port range ({start}-{end}) was invalid. either the start was 0 or it was greater than the end.")]
InvalidRange { start: u16, end: u16 },
#[error("could not parse '{raw}' into a valid policy address:port pattern")]
MalformedAddressPortPattern { raw: String },
#[error("could not parse '{raw}' into a valid address policy")]
MalformedAddressPolicy { raw: String },
#[error(
"the provided exit policy entry does not start with the expected '{}' prefix: '{entry}'",
EXIT_POLICY_FIELD_NAME
)]
NoExitPolicyPrefix { entry: String },
}
-14
View File
@@ -1,14 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// adapted from: https://github.com/dgoulet-tor/arti/tree/781dc4bd64f515f0c13ae9907c473c2bad8fbf71
// and https://github.com/torproject/tor/blob/3cb6a690be60fcdab60130402ff88dcfc0657596/contrib/or-tools/exitlist
// + https://github.com/torproject/tor/blob/3cb6a690be60fcdab60130402ff88dcfc0657596/src/feature/dirparse/policy_parse.c
mod address_policy;
mod error;
pub use address_policy::{
AddressPolicy, AddressPolicyAction, AddressPolicyRule, AddressPortPattern, IpPattern, PortRange,
};
pub use error::PolicyError;
-25
View File
@@ -1,25 +0,0 @@
[package]
name = "http-api-client"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
url = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
features = ["tokio"]
-516
View File
@@ -1,516 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use reqwest::{IntoUrl, Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error;
use tracing::warn;
use url::Url;
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
pub type PathSegments<'a> = &'a [&'a str];
pub type Params<'a, K, V> = &'a [(K, V)];
pub const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
#[derive(Debug, Error)]
pub enum HttpClientError<E: Display = String> {
#[error("there was an issue with the REST request: {source}")]
ReqwestClientError {
#[from]
source: reqwest::Error,
},
#[error("provided url is malformed: {source}")]
MalformedUrl {
#[from]
source: url::ParseError,
},
#[error("the requested resource could not be found")]
NotFound,
#[error("request failed with error message: {0}")]
GenericRequestFailure(String),
#[error("the request failed with status '{status}'. no additional error message provided")]
RequestFailure { status: StatusCode },
#[error("the returned response was empty. status: '{status}'")]
EmptyResponse { status: StatusCode },
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
EndpointFailure { status: StatusCode, error: E },
#[cfg(target_arch = "wasm32")]
#[error("the request has timed out")]
RequestTimeout,
}
/// A simple extendable client wrapper for http request with extra url sanitization.
#[derive(Debug, Clone)]
pub struct Client {
base_url: Url,
reqwest_client: reqwest::Client,
#[cfg(target_arch = "wasm32")]
request_timeout: Duration,
}
impl Client {
// no timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
pub fn new(base_url: Url, timeout: Option<Duration>) -> Self {
#[cfg(target_arch = "wasm32")]
let reqwest_client = reqwest::Client::new();
// TODO: we should probably be propagating the error rather than panicking,
// but that'd break bunch of things due to type changes
#[cfg(not(target_arch = "wasm32"))]
let reqwest_client = reqwest::ClientBuilder::new()
.timeout(timeout.unwrap_or(DEFAULT_TIMEOUT))
.user_agent(format!("nym-http-api-client/{}", env!("CARGO_PKG_VERSION")))
.build()
.expect("Client::new()");
Client {
base_url,
reqwest_client,
#[cfg(target_arch = "wasm32")]
request_timeout: timeout.unwrap_or(DEFAULT_TIMEOUT),
}
}
pub fn new_url<U, E>(url: U, timeout: Option<Duration>) -> Result<Self, HttpClientError<E>>
where
U: IntoUrl,
E: Display,
{
// a naive check: if the provided URL does not start with http(s), add that scheme
let str_url = url.as_str();
if !str_url.starts_with("http") {
let alt = format!("http://{str_url}");
warn!("the provided url ('{str_url}') does not contain scheme information. Changing it to '{alt}' ...");
// TODO: or should we maybe default to https?
Self::new_url(alt, timeout)
} else {
Ok(Self::new(url.into_url()?, timeout))
}
}
pub fn change_base_url(&mut self, new_url: Url) {
self.base_url = new_url
}
pub fn current_url(&self) -> &Url {
&self.base_url
}
async fn send_get_request<K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, HttpClientError<E>>
where
K: AsRef<str>,
V: AsRef<str>,
E: Display,
{
let url = sanitize_url(&self.base_url, path, params);
#[cfg(target_arch = "wasm32")]
{
Ok(
wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client.get(url).send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??,
)
}
#[cfg(not(target_arch = "wasm32"))]
{
Ok(self.reqwest_client.get(url).send().await?)
}
}
async fn send_post_request<B, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<Response, HttpClientError<E>>
where
B: Serialize + ?Sized,
K: AsRef<str>,
V: AsRef<str>,
E: Display,
{
let url = sanitize_url(&self.base_url, path, params);
#[cfg(target_arch = "wasm32")]
{
Ok(wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client.post(url).json(json_body).send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
}
#[cfg(not(target_arch = "wasm32"))]
{
Ok(self.reqwest_client.post(url).json(json_body).send().await?)
}
}
pub async fn get_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
E: Display + DeserializeOwned,
{
let res = self.send_get_request(path, params).await?;
parse_response(res, false).await
}
pub async fn post_json<B, T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
E: Display + DeserializeOwned,
{
let res = self.send_post_request(path, params, json_body).await?;
parse_response(res, true).await
}
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str>,
{
#[cfg(target_arch = "wasm32")]
let res = {
wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client
.get(self.base_url.join(endpoint.as_ref())?)
.send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??
};
#[cfg(not(target_arch = "wasm32"))]
let res = {
self.reqwest_client
.get(self.base_url.join(endpoint.as_ref())?)
.send()
.await?
};
parse_response(res, false).await
}
pub async fn post_json_endpoint<B, T, S, E>(
&self,
endpoint: S,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str>,
{
#[cfg(target_arch = "wasm32")]
let res = {
wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client
.post(self.base_url.join(endpoint.as_ref())?)
.json(json_body)
.send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??
};
#[cfg(not(target_arch = "wasm32"))]
let res = {
self.reqwest_client
.post(self.base_url.join(endpoint.as_ref())?)
.json(json_body)
.send()
.await?
};
parse_response(res, true).await
}
}
// define those methods on the trait for nicer extensions (and not having to type the thing twice)
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait ApiClient {
/// 'get' json data from the segment-defined path, i.e. for example `["api", "v1", "mixnodes"]`,
/// with tuple defined key-value parameters, i.e. for example `[("since", "12345")]`
async fn get_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned;
async fn post_json<B, T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned;
/// `get` json data from the provided absolute endpoint, i.e. for example `"/api/v1/mixnodes?since=12345"`
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send;
async fn post_json_data_to<B, T, S, E>(
&self,
endpoint: S,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send;
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl ApiClient for Client {
async fn get_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned,
{
self.get_json(path, params).await
}
async fn post_json<B, T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned,
{
self.post_json(path, params, json_body).await
}
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send,
{
self.get_json_endpoint(endpoint).await
}
async fn post_json_data_to<B, T, S, E>(
&self,
endpoint: S,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send,
{
self.post_json_endpoint(endpoint, json_body).await
}
}
// utility function that should solve the double slash problem in API urls forever.
pub fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
base: &Url,
segments: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Url {
let mut url = base.clone();
let mut path_segments = url
.path_segments_mut()
.expect("provided validator url does not have a base!");
path_segments.pop_if_empty();
for segment in segments {
let segment = segment.strip_prefix('/').unwrap_or(segment);
let segment = segment.strip_suffix('/').unwrap_or(segment);
path_segments.push(segment);
}
// I don't understand why compiler couldn't figure out that it's no longer used
// and can be dropped
drop(path_segments);
if !params.is_empty() {
url.query_pairs_mut().extend_pairs(params);
}
url
}
async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
where
T: DeserializeOwned,
E: DeserializeOwned + Display,
{
let status = res.status();
if !allow_empty {
if let Some(0) = res.content_length() {
return Err(HttpClientError::EmptyResponse { status });
}
}
if res.status().is_success() {
Ok(res.json().await?)
} else if res.status() == StatusCode::NOT_FOUND {
Err(HttpClientError::NotFound)
} else {
let Ok(plaintext) = res.text().await else {
return Err(HttpClientError::RequestFailure { status });
};
if let Ok(request_error) = serde_json::from_str(&plaintext) {
Err(HttpClientError::EndpointFailure {
status,
error: request_error,
})
} else {
Err(HttpClientError::GenericRequestFailure(plaintext))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sanitizing_urls() {
let base_url: Url = "http://foomp.com".parse().unwrap();
// works with 1 segment
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["foo"], NO_PARAMS).as_str()
);
// works with 2 segments
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
);
// works with leading slash
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["/foo"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
);
// works with trailing slash
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
);
// works with both leading and trailing slash
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
);
// adds params
assert_eq!(
"http://foomp.com/foo/bar?foomp=baz",
sanitize_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
sanitize_url(
&base_url,
&["/foo/", "/bar/"],
&[("arg1", "val1"), ("arg2", "val2")]
)
.as_str()
);
}
}
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "nym-http-requests"
version = "0.1.0"
description = "Helper library for sending HTTP requesters over the Nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
[dependencies]
nym-socks5-requests = { path = "../socks5/requests" }
nym-ordered-buffer = { path = "../socks5/ordered-buffer" }
nym-service-providers-common = { path = "../../service-providers/common" }
bytecodec = "0.4.15"
httpcodec = "0.2.3"
bytes = "1"
http = "0.2.9"
thiserror = "1"
url = "2"
+23
View File
@@ -0,0 +1,23 @@
use std::io;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MixHttpRequestError {
#[error("invalid Socks5 response")]
InvalidSocks5Response,
#[error("the received Socks5 response was empty")]
EmptySocks5Response,
#[error("bytecodec Error: {0}")]
ByteCodecError(#[from] bytecodec::Error),
#[error("Url parse error: {0}")]
UrlParseError(#[from] url::ParseError),
#[error("could not resolve socket address from the provided URL")]
SocketAddrResolveError {
#[source]
source: io::Error,
},
}
+63
View File
@@ -0,0 +1,63 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bytecodec::bytes::BytesEncoder;
use bytecodec::io::IoEncodeExt;
use bytecodec::Encode;
use httpcodec::{BodyEncoder, Request, RequestEncoder};
pub mod error;
pub mod socks;
pub fn encode_http_request_as_string(
request: Request<Vec<u8>>,
) -> Result<String, error::MixHttpRequestError> {
// Encode HTTP request as bytes
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
encoder.start_encoding(request)?;
let mut buf = Vec::new();
encoder.encode_all(&mut buf)?;
Ok(String::from_utf8_lossy(&buf).to_string())
}
#[cfg(test)]
mod http_requests_tests {
use super::*;
use httpcodec::{HeaderField, HttpVersion, Method, RequestTarget};
fn create_http_get_request() -> Request<Vec<u8>> {
let mut request = Request::new(
Method::new("GET").unwrap(),
RequestTarget::new("/.wellknown/wallet/validators.json").unwrap(),
HttpVersion::V1_1,
b"".to_vec(),
);
let mut headers = request.header_mut();
headers.add_field(HeaderField::new("Host", "nymtech.net").unwrap());
request
}
#[test]
fn http_request_ok() {
// Encode HTTP request as bytes
let request = create_http_get_request();
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
encoder.start_encoding(request).unwrap();
let mut buf = Vec::new();
encoder.encode_all(&mut buf).unwrap();
let body_as_string = String::from_utf8(buf).unwrap();
// replace newlines with \r\n
let expected = r"GET /.wellknown/wallet/validators.json HTTP/1.1
Host: nymtech.net
Content-Length: 0
"
.replace('\n', "\r\n");
assert_eq!(expected, body_as_string);
}
}
+214
View File
@@ -0,0 +1,214 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error;
use bytecodec::bytes::BytesEncoder;
use bytecodec::bytes::RemainingBytesDecoder;
use bytecodec::io::IoEncodeExt;
use bytecodec::{DecodeExt, Encode};
use httpcodec::{BodyDecoder, ResponseDecoder};
use httpcodec::{BodyEncoder, Request, RequestEncoder};
use nym_service_providers_common::interface::ProviderInterfaceVersion;
use nym_socks5_requests::{SocketData, Socks5ProtocolVersion, Socks5ProviderRequest};
pub fn encode_http_request_as_socks_send_request(
provider_interface: ProviderInterfaceVersion,
socks5_protocol: Socks5ProtocolVersion,
conn_id: u64,
request: Request<Vec<u8>>,
seq: Option<u64>,
local_closed: bool,
) -> Result<nym_socks5_requests::Socks5ProviderRequest, error::MixHttpRequestError> {
// Encode HTTP request as bytes
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
encoder.start_encoding(request)?;
let mut buf = Vec::new();
encoder.encode_all(&mut buf)?;
// Wrap it as SOCKS send request
let request_content = nym_socks5_requests::request::Socks5Request::new_send(
socks5_protocol,
SocketData::new(seq.unwrap_or_default(), conn_id, local_closed, buf),
);
// and wrap it in provider request
Ok(Socks5ProviderRequest::new_provider_data(
provider_interface,
request_content,
))
}
#[derive(Debug)]
pub struct MixHttpResponse {
// pub connection_id: u64,
// #[deprecated]
// pub is_closed: bool,
pub http_response: httpcodec::Response<Vec<u8>>,
// #[deprecated]
// pub seq: u64,
}
impl MixHttpResponse {
pub fn try_from_bytes(b: &[u8]) -> Result<MixHttpResponse, error::MixHttpRequestError> {
if b.is_empty() {
Err(error::MixHttpRequestError::EmptySocks5Response)
} else {
let mut decoder = ResponseDecoder::<BodyDecoder<RemainingBytesDecoder>>::default();
let http_response = decoder.decode_from_bytes(b)?;
Ok(MixHttpResponse { http_response })
}
}
}
// impl TryFrom<Socks5Response> for MixHttpResponse {
// type Error = error::MixHttpRequestError;
//
// fn try_from(value: Socks5Response) -> Result<Self, Self::Error> {
// if let Socks5ResponseContent::NetworkData { content } = value.content {
// content.try_into()
// } else {
// Err(error::MixHttpRequestError::InvalidSocks5Response)
// }
// }
// }
//
// impl TryFrom<SocketData> for MixHttpResponse {
// type Error = error::MixHttpRequestError;
//
// fn try_from(value: SocketData) -> Result<Self, Self::Error> {
// if value.data.is_empty() {
// Err(error::MixHttpRequestError::EmptySocks5Response)
// } else {
// let mut decoder = ResponseDecoder::<BodyDecoder<RemainingBytesDecoder>>::default();
// let http_response = decoder.decode_from_bytes(value.data.as_ref())?;
//
// Ok(MixHttpResponse {
// connection_id: value.header.connection_id,
// is_closed: value.header.local_socket_closed,
// http_response,
// seq: value.header.seq,
// })
// }
// }
// }
// pub fn decode_socks_response_as_http_response(
// socks5_response: Socks5Response,
// ) -> Result<MixHttpResponse, error::MixHttpRequestError> {
// socks5_response.try_into()
// }
//
// #[cfg(test)]
// mod http_requests_tests {
// use super::*;
// use httpcodec::{HeaderField, HttpVersion, Method, RequestTarget};
// use nym_service_providers_common::interface::Serializable;
// use nym_socks5_requests::Socks5Response;
//
// fn create_http_get_request() -> Request<Vec<u8>> {
// let mut request = Request::new(
// Method::new("GET").unwrap(),
// RequestTarget::new("/.wellknown/wallet/validators.json").unwrap(),
// HttpVersion::V1_1,
// b"".to_vec(),
// );
// let mut headers = request.header_mut();
// headers.add_field(HeaderField::new("Host", "nymtech.net").unwrap());
//
// request
// }
//
// fn create_socks5_request_buffer() -> Vec<u8> {
// let request = create_http_get_request();
// let socks5_request = encode_http_request_as_socks_send_request(
// ProviderInterfaceVersion::new_current(),
// Socks5ProtocolVersion::new_current(),
// 99u64,
// request,
// Some(42u64),
// true,
// )
// .unwrap();
// socks5_request.into_bytes()
// }
//
// #[test]
// fn request_http_request_content_ok() {
// let buffer = create_socks5_request_buffer();
//
// // HTTP request string content is as expected
// assert_eq!(
// [71u8, 69u8, 84u8, 32u8, 47u8, 46u8, 119u8, 101u8],
// buffer[19..27]
// );
// }
//
// /// This test will fail if the framing of the request buffer changes, e.g. when OrderedMessage
// /// changes to have the `index` value as a field, instead of packed with the `data`
// #[test]
// fn request_size_as_expected_ok() {
// let buffer = create_socks5_request_buffer();
// // println!("{:?}", buffer) // uncomment and run `cargo test -- --nocapture` to view
//
// assert_eq!(108, buffer.len()); // version set to SOCKS5
// }
//
// #[test]
// fn request_socks5_headers_ok() {
// let buffer = create_socks5_request_buffer();
//
// assert_eq!(5u8, buffer[0]); // version set to SOCKS5
// assert_eq!(1u8, buffer[1]); // type is SEND
// assert_eq!(99u8, buffer[9]); // ConnectionId is correct
// assert_eq!(1u8, buffer[10]); // local_closed is true
// }
//
// #[test]
// fn request_ordered_message_ok() {
// let buffer = create_socks5_request_buffer();
//
// // OrderedMessage index is correct
// assert_eq!(42u8, buffer[18]);
// }
//
// fn create_socks_response() -> Socks5Response {
// // HTTP response is just a string
// let http_response_string = "HTTP/1.1 200 OK\r\nServer: foo/0.0.1\r\n\r\n";
//
// let data = http_response_string.as_bytes().to_vec();
//
// // wrap in `NetworkData`, then Socks5Response
// Socks5Response::new(
// Socks5ProtocolVersion::new_current(),
// Socks5ResponseContent::NetworkData {
// content: SocketData::new(42, 99u64, false, data),
// },
// )
// }
//
// /// This test will fail is anything in the framing of the socks5_response byte
// /// representation changes
// #[test]
// fn response_byte_size_is_as_expected() {
// let socks5_response = create_socks_response();
// let buf = socks5_response.into_bytes();
//
// assert_eq!(57, buf.len());
// }
//
// #[test]
// fn response_parses() {
// unimplemented!()
// // let socks5_response = create_socks_response();
// // let response = decode_socks_response_as_http_response(socks5_response).unwrap();
// //
// // assert_eq!(42u64, response.seq); // OrderedMessage index as expected
// // assert_eq!(HttpVersion::V1_1, response.http_response.http_version());
// // assert_eq!(200u16, response.http_response.status_code().as_u16());
// // assert_eq!(
// // "foo/0.0.1",
// // response.http_response.header().get_field("Server").unwrap()
// // );
// }
// }
+1 -1
View File
@@ -10,4 +10,4 @@ bip32 = "0.5.1"
k256 = { workspace = true }
ledger-transport = "0.10.0"
ledger-transport-hid = "0.10.0"
thiserror = { workspace = true }
thiserror = "1"
+2 -2
View File
@@ -23,7 +23,7 @@ impl EchoPacket {
.chain(keys.public_key().to_bytes().iter().cloned())
.collect::<Vec<_>>();
let signature = keys.private_key().sign(bytes_to_sign);
let signature = keys.private_key().sign(&bytes_to_sign);
EchoPacket {
sequence_number,
@@ -67,7 +67,7 @@ impl EchoPacket {
pub(crate) fn construct_reply(self, private_key: &identity::PrivateKey) -> ReplyPacket {
let bytes = self.to_bytes();
let signature = private_key.sign(bytes);
let signature = private_key.sign(&bytes);
ReplyPacket {
base_packet: self,
signature,
+1 -1
View File
@@ -7,5 +7,5 @@ edition = "2021"
[dependencies]
async-trait = { workspace = true }
thiserror = { workspace = true }
thiserror = "1.0"
+1 -1
View File
@@ -12,7 +12,7 @@ cfg-if = { workspace = true }
dotenvy = { workspace = true }
hex-literal = "0.3.3"
once_cell = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"] }
schemars = { version = "0.8", features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"]}
thiserror = { workspace = true }
url = { workspace = true }
-3
View File
@@ -462,9 +462,6 @@ pub const DEFAULT_NYM_API_PORT: u16 = 8080;
pub const NYM_API_VERSION: &str = "v1";
// NYM-NODE
pub const DEFAULT_NYM_NODE_HTTP_PORT: u16 = 8080;
// REWARDING
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have reliable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
-6
View File
@@ -27,10 +27,6 @@ pub const NYXD_URL: &str = "https://rpc.nymtech.net";
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
pub const EXIT_POLICY_URL: &str =
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(NYXD_URL, Some(NYM_API))]
}
@@ -105,7 +101,6 @@ pub fn export_to_env() {
set_var_to_default(var_names::NYXD, NYXD_URL);
set_var_to_default(var_names::NYM_API, NYM_API);
set_var_to_default(var_names::EXPLORER_API, EXPLORER_API);
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
}
pub fn export_to_env_if_not_set() {
@@ -153,5 +148,4 @@ pub fn export_to_env_if_not_set() {
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
set_var_conditionally_to_default(var_names::EXPLORER_API, EXPLORER_API);
set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
}
-1
View File
@@ -27,7 +27,6 @@ pub const NAME_SERVICE_CONTRACT_ADDRESS: &str = "NAME_SERVICE_CONTRACT_ADDRESS";
pub const NYXD: &str = "NYXD";
pub const NYM_API: &str = "NYM_API";
pub const EXPLORER_API: &str = "EXPLORER_API";
pub const EXIT_POLICY_URL: &str = "EXIT_POLICY";
pub const DKG_TIME_CONFIGURATION: &str = "DKG_TIME_CONFIGURATION";
+2 -2
View File
@@ -11,8 +11,8 @@ bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="gt-seriali
itertools = "0.10"
digest = "0.9"
rand = "0.8"
thiserror = { workspace = true }
serde = { workspace = true }
thiserror = "1.0"
serde = "1.0"
serde_derive = "1.0"
bs58 = "0.4.0"
sha2 = "0.9"
+1 -1
View File
@@ -11,7 +11,7 @@ repository = { workspace = true }
nym-crypto = { path = "../../crypto", features = ["asymmetric"] } # all addresses are expressed in terms on their crypto keys
nym-sphinx-types = { path = "../types", features = ["sphinx"] } # we need to be able to refer to some types defined inside sphinx crate
serde = "1.0" # implementing serialization/deserialization for some types, like `Recipient`
thiserror = { workspace = true }
thiserror = "1.0.37"
[dev-dependencies]
rand = "0.7"
@@ -10,8 +10,8 @@ repository = { workspace = true }
[dependencies]
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
bs58 = "0.4"
serde = { workspace = true }
thiserror = { workspace = true }
serde = "1.0"
thiserror = "1"
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
nym-sphinx-addressing = { path = "../addressing" }
+1 -1
View File
@@ -12,7 +12,7 @@ repository = { workspace = true }
[dependencies]
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
thiserror = { workspace = true }
thiserror = "1.0.37"
nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" }

Some files were not shown because too many files have changed in this diff Show More