Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5bed9d3e2 | |||
| 1119505990 | |||
| f0de459c58 | |||
| b4c525b664 | |||
| 07291fd0ad | |||
| 36c3604bb6 | |||
| 5a32f5da68 | |||
| 66f77d481c |
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
name: 'Documentation'
|
|
||||||
about: Suggest a fix or enhancement to the documentation or developer portal content
|
|
||||||
title: "[DOCS]"
|
|
||||||
labels: documentation
|
|
||||||
assignees: mfahampshire
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Is your issue either:
|
|
||||||
- [ ] a fix to existing documentation (e.g. fixing a broken link or incorrect command)
|
|
||||||
- [ ] an enhancement (e.g. adding a description for an undocumented feature)
|
|
||||||
|
|
||||||
Please briefly describe your issue:
|
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
- name: Install Rust stable
|
- name: Install Rust stable
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 1.69.0
|
toolchain: stable
|
||||||
|
|
||||||
- name: Build all binaries
|
- name: Build all binaries
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
- name: Install Rust stable
|
- name: Install Rust stable
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 1.69.0
|
toolchain: stable
|
||||||
target: wasm32-unknown-unknown
|
target: wasm32-unknown-unknown
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
@@ -107,8 +107,6 @@ jobs:
|
|||||||
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
|
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
|
||||||
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
|
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
|
||||||
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
||||||
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
|
|
||||||
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
|
|
||||||
|
|
||||||
- name: Deploy branch to CI www
|
- name: Deploy branch to CI www
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|||||||
@@ -9,14 +9,12 @@ on:
|
|||||||
- 'gateway/**'
|
- 'gateway/**'
|
||||||
- 'integrations/**'
|
- 'integrations/**'
|
||||||
- 'mixnode/**'
|
- 'mixnode/**'
|
||||||
- 'sdk/lib/socks5-listener/**'
|
|
||||||
- 'sdk/rust/nym-sdk/**'
|
- 'sdk/rust/nym-sdk/**'
|
||||||
- 'service-providers/**'
|
- 'service-providers/**'
|
||||||
- 'nym-api/**'
|
- 'nym-api/**'
|
||||||
- 'nym-outfox/**'
|
- 'nym-outfox/**'
|
||||||
- 'tools/nym-cli/**'
|
- 'tools/nym-cli/**'
|
||||||
- 'tools/ts-rs-cli/**'
|
- 'tools/ts-rs-cli/**'
|
||||||
- 'Cargo.toml'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'clients/**'
|
- 'clients/**'
|
||||||
@@ -25,14 +23,12 @@ on:
|
|||||||
- 'gateway/**'
|
- 'gateway/**'
|
||||||
- 'integrations/**'
|
- 'integrations/**'
|
||||||
- 'mixnode/**'
|
- 'mixnode/**'
|
||||||
- 'sdk/lib/socks5-listener/**'
|
|
||||||
- 'sdk/rust/nym-sdk/**'
|
- 'sdk/rust/nym-sdk/**'
|
||||||
- 'service-providers/**'
|
- 'service-providers/**'
|
||||||
- 'nym-api/**'
|
- 'nym-api/**'
|
||||||
- 'nym-outfox/**'
|
- 'nym-outfox/**'
|
||||||
- 'tools/nym-cli/**'
|
- 'tools/nym-cli/**'
|
||||||
- 'tools/ts-rs-cli/**'
|
- 'tools/ts-rs-cli/**'
|
||||||
- 'Cargo.toml'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
name: Nym Connect - Android APK Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "release/nc-android-v[0-9].[0-9].[0-9]*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build APK
|
||||||
|
runs-on: custom-runner-linux
|
||||||
|
env:
|
||||||
|
ANDROID_HOME: ${{ github.workspace }}/android-sdk
|
||||||
|
NDK_VERSION: 25.1.8937393
|
||||||
|
NDK_HOME: ${{ github.workspace }}/android-sdk/ndk/25.1.8937393
|
||||||
|
SDK_PLATFORM_VERSION: android-33
|
||||||
|
SDK_BUILDTOOLS_VERSION: 33.0.1
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install Dependencies (Linux)
|
||||||
|
# https://next--tauri.netlify.app/next/guides/getting-started/prerequisites/linux/#1-system-dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get -y install \
|
||||||
|
build-essential \
|
||||||
|
unzip \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
libssl-dev \
|
||||||
|
squashfs-tools \
|
||||||
|
librsvg2-dev
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: "temurin"
|
||||||
|
java-version: "17"
|
||||||
|
|
||||||
|
- name: Install Android SDK manager
|
||||||
|
# https://developer.android.com/studio/command-line/sdkmanager
|
||||||
|
run: |
|
||||||
|
curl -sS https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -o cmdline-tools.zip
|
||||||
|
unzip cmdline-tools.zip
|
||||||
|
mkdir -p $ANDROID_HOME/cmdline-tools/latest
|
||||||
|
mv cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest
|
||||||
|
rm -rf cmdline-tools
|
||||||
|
|
||||||
|
- name: Install Android S/NDK
|
||||||
|
run: |
|
||||||
|
echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses
|
||||||
|
echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
|
||||||
|
"platforms;$SDK_PLATFORM_VERSION" \
|
||||||
|
"platform-tools" \
|
||||||
|
"ndk;$NDK_VERSION" \
|
||||||
|
"build-tools;$SDK_BUILDTOOLS_VERSION"
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
# TODO this step takes a considerable amount of time
|
||||||
|
# We could avoid to compile from source tauri-cli and use instead
|
||||||
|
# pre-compiled binary provided by the node package `@tauri-apps/cli`
|
||||||
|
# But when using the later the build fails for some reason
|
||||||
|
# so keep installing and using tauri-cli
|
||||||
|
- name: Install tauri cli
|
||||||
|
run: cargo install tauri-cli --version "^2.0.0-alpha.2"
|
||||||
|
|
||||||
|
- name: Install rust android targets
|
||||||
|
run: |
|
||||||
|
rustup target add aarch64-linux-android \
|
||||||
|
armv7-linux-androideabi \
|
||||||
|
i686-linux-android \
|
||||||
|
x86_64-linux-android
|
||||||
|
|
||||||
|
- name: Setup Nodejs
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
|
||||||
|
- name: Install yarn
|
||||||
|
run: |
|
||||||
|
npm i -g yarn
|
||||||
|
yarn --version
|
||||||
|
|
||||||
|
- name: Build frontend code
|
||||||
|
run: |
|
||||||
|
yarn install --frozen-lockfile
|
||||||
|
yarn build
|
||||||
|
yarn workspace @nym/nym-connect-mobile webpack:prod
|
||||||
|
|
||||||
|
- name: Build APK
|
||||||
|
working-directory: nym-connect/mobile
|
||||||
|
env:
|
||||||
|
# NODE_TAURI_CLI=${{ github.workspace }}/nym-connect/mobile/node_modules/.bin/tauri
|
||||||
|
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
||||||
|
WRY_ANDROID_PACKAGE: net.nymtech.nym_connect
|
||||||
|
WRY_ANDROID_LIBRARY: nym_connect
|
||||||
|
# TODO build with release profile (--release), it will requires
|
||||||
|
# to sign the APK. For now build with debug profile to avoid that
|
||||||
|
# TODO build using `yarn tauri`, provide NODE_TAURI_CLI, see TODO notes above
|
||||||
|
run: cargo tauri android build --debug --apk --split-per-abi -t aarch64
|
||||||
|
|
||||||
|
# TODO add the version number to APK name
|
||||||
|
- name: Rename APK artifact
|
||||||
|
run: |
|
||||||
|
mkdir apk/
|
||||||
|
mv nym-connect/mobile/src-tauri/gen/android/nym_connect/app/build/outputs/apk/arm64/debug/app-arm64-debug.apk \
|
||||||
|
apk/nym-connect-arm64-debug.apk
|
||||||
|
mv nym-connect/mobile/src-tauri/gen/android/nym_connect/app/build/outputs/apk/x86_64/debug/app-x86_64-debug.apk \
|
||||||
|
apk/nym-connect-x86_64-debug.apk
|
||||||
|
|
||||||
|
- name: Upload APK artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: nc-apk-debug
|
||||||
|
path: |
|
||||||
|
apk/nym-connect-arm64-debug.apk
|
||||||
|
apk/nym-connect-x86_64-debug.apk
|
||||||
|
|
||||||
|
# publish:
|
||||||
|
# name: Publish APK
|
||||||
|
# needs: build
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# steps:
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@v3
|
||||||
|
# - name: Download binary artifact
|
||||||
|
# uses: actions/download-artifact@v3
|
||||||
|
# with:
|
||||||
|
# name: nc-apk-debug
|
||||||
|
# path: apk
|
||||||
|
# # TODO add a step to upload the APK somewhere
|
||||||
|
# - name: Publish
|
||||||
|
# uses: ???
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
name: Nyms5 Android
|
|
||||||
# unsigned APKs only, supported archs:
|
|
||||||
# - arm64-v8a (arm64)
|
|
||||||
# - x86_64
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- nyms5-android-v*
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build APK
|
|
||||||
runs-on: custom-runner-linux
|
|
||||||
env:
|
|
||||||
ANDROID_HOME: ${{ github.workspace }}/android-sdk
|
|
||||||
NDK_VERSION: 25.2.9519653
|
|
||||||
NDK_HOME: ${{ github.workspace }}/android-sdk/ndk/25.2.9519653
|
|
||||||
SDK_PLATFORM_VERSION: android-33
|
|
||||||
SDK_BUILDTOOLS_VERSION: 33.0.2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Install Java
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: "temurin"
|
|
||||||
java-version: "17"
|
|
||||||
|
|
||||||
- name: Install Android SDK manager
|
|
||||||
# https://developer.android.com/studio/command-line/sdkmanager
|
|
||||||
run: |
|
|
||||||
curl -sS https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -o cmdline-tools.zip
|
|
||||||
unzip cmdline-tools.zip
|
|
||||||
mkdir -p $ANDROID_HOME/cmdline-tools/latest
|
|
||||||
mv cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest
|
|
||||||
rm -rf cmdline-tools
|
|
||||||
|
|
||||||
- name: Install Android S/NDK
|
|
||||||
run: |
|
|
||||||
echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses
|
|
||||||
echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
|
|
||||||
"platforms;$SDK_PLATFORM_VERSION" \
|
|
||||||
"platform-tools" \
|
|
||||||
"ndk;$NDK_VERSION" \
|
|
||||||
"build-tools;$SDK_BUILDTOOLS_VERSION"
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@1.70.0
|
|
||||||
|
|
||||||
- name: Install rust android targets
|
|
||||||
run: |
|
|
||||||
rustup target add aarch64-linux-android \
|
|
||||||
x86_64-linux-android
|
|
||||||
|
|
||||||
- name: Build lib nym-socks5-listener
|
|
||||||
working-directory: sdk/lib/socks5-listener/
|
|
||||||
env:
|
|
||||||
RELEASE: true
|
|
||||||
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
|
|
||||||
# build for arm64 and x86_64
|
|
||||||
run: ./build-android.sh aarch64 x86_64
|
|
||||||
|
|
||||||
- name: Build APKs (unsigned)
|
|
||||||
working-directory: nym-connect/native/android
|
|
||||||
env:
|
|
||||||
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
|
||||||
# build for arm64 and x86_64
|
|
||||||
run: |
|
|
||||||
./gradlew :app:assembleArch64Debug
|
|
||||||
./gradlew :app:assembleArch64Release
|
|
||||||
|
|
||||||
- name: Prepare APKs
|
|
||||||
run: |
|
|
||||||
mkdir apk
|
|
||||||
mv nym-connect/native/android/app/build/outputs/apk/arch64/debug/app-arch64-debug.apk \
|
|
||||||
apk/nyms5-arch64-debug.apk
|
|
||||||
mv nym-connect/native/android/app/build/outputs/apk/arch64/release/app-arch64-release-unsigned.apk \
|
|
||||||
apk/nyms5-arch64-release.apk
|
|
||||||
|
|
||||||
- name: Upload APKs
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: nyms5-apk-arch64
|
|
||||||
path: |
|
|
||||||
apk/nyms5-arch64-debug.apk
|
|
||||||
apk/nyms5-arch64-release.apk
|
|
||||||
|
|
||||||
gh-release:
|
|
||||||
name: Publish APK (GH release)
|
|
||||||
needs: build
|
|
||||||
runs-on: custom-runner-linux
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Download binary artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: nyms5-apk-arch64
|
|
||||||
path: apk
|
|
||||||
- name: Release
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
apk/nyms5-arch64-debug.apk
|
|
||||||
apk/nyms5-arch64-release.apk
|
|
||||||
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
name: Upload nyxd to CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-nyxd:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: [ubuntu-20.04]
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Prepare build output directory
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
OUTPUT_DIR: ci-builds/nyxd
|
|
||||||
run: |
|
|
||||||
rm -rf ci-builds || true
|
|
||||||
mkdir -p $OUTPUT_DIR
|
|
||||||
echo $OUTPUT_DIR
|
|
||||||
|
|
||||||
- name: Install Dependencies (Linux)
|
|
||||||
run: sudo apt-get update && sudo apt-get -y install build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools git
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Update env variables to include go
|
|
||||||
run: |
|
|
||||||
sudo rm -rf /usr/local/go
|
|
||||||
curl https://dl.google.com/go/go1.19.2.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf -
|
|
||||||
cat <<'EOF' >>$HOME/.profile
|
|
||||||
export GOROOT=/usr/local/go
|
|
||||||
export GOPATH=$HOME/go
|
|
||||||
export GO111MODULE=on
|
|
||||||
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
|
|
||||||
EOF
|
|
||||||
source $HOME/.profile
|
|
||||||
|
|
||||||
- name: Verify Go is installed
|
|
||||||
run: go version
|
|
||||||
|
|
||||||
- name: Clone nyxd repo
|
|
||||||
run: |
|
|
||||||
git clone https://github.com/tommyv1987/nyxd
|
|
||||||
cd nyxd
|
|
||||||
git checkout release/v0.30.2
|
|
||||||
|
|
||||||
- name: Run nyxd
|
|
||||||
run: |
|
|
||||||
pwd
|
|
||||||
cd nyxd && make build
|
|
||||||
sleep 10
|
|
||||||
ls /home/runner/work/nym/nym/nyxd/build
|
|
||||||
|
|
||||||
- name: Prepare build output
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
OUTPUT_DIR: ci-builds/nyxd
|
|
||||||
run: |
|
|
||||||
cp /home/runner/work/nym/nym/nyxd/build/nyxd $OUTPUT_DIR
|
|
||||||
WASMVM_SO=$(ldd /home/runner/work/nym/nym/nyxd/build/nyxd | grep "libwasm*" | awk '{ print $3 }')
|
|
||||||
ls $WASMVM_SO
|
|
||||||
sleep 3
|
|
||||||
cp $(echo $WASMVM_SO) $OUTPUT_DIR
|
|
||||||
|
|
||||||
- name: Deploy nyxd to CI www
|
|
||||||
continue-on-error: true
|
|
||||||
uses: easingthemes/ssh-deploy@main
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
|
||||||
ARGS: "-avzr"
|
|
||||||
SOURCE: "ci-builds/"
|
|
||||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
|
||||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
|
||||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
|
|
||||||
EXCLUDE: "/dist/, /node_modules/"
|
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
.idea
|
.idea
|
||||||
target
|
target
|
||||||
.env
|
.env
|
||||||
.env.dev
|
|
||||||
/.vscode/settings.json
|
/.vscode/settings.json
|
||||||
validator/.vscode
|
validator/.vscode
|
||||||
sample-configs/validator-config.toml
|
sample-configs/validator-config.toml
|
||||||
|
|||||||
-103
@@ -4,109 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [v1.1.22] (2023-06-20)
|
|
||||||
|
|
||||||
- CLI tool for querying network-requesters ([#3539])
|
|
||||||
- Statically link OpenSSL ([#3510])
|
|
||||||
- NymConnect - add sentry.io reporting ([#3421])
|
|
||||||
- init command does not change version number in config.toml ([#3336])
|
|
||||||
- [Bug] Config version does not correspond to binary version ([#3434])
|
|
||||||
|
|
||||||
[#3539]: https://github.com/nymtech/nym/issues/3539
|
|
||||||
[#3510]: https://github.com/nymtech/nym/issues/3510
|
|
||||||
[#3421]: https://github.com/nymtech/nym/issues/3421
|
|
||||||
[#3336]: https://github.com/nymtech/nym/issues/3336
|
|
||||||
[#3434]: https://github.com/nymtech/nym/issues/3434
|
|
||||||
|
|
||||||
## [v1.1.21] (2023-06-13)
|
|
||||||
|
|
||||||
- mixFetch: Change socks5 `SendRequest` to include OrderedMessage index as a field rather than making it serialized inside the `data` field
|
|
||||||
([#3534])
|
|
||||||
- Explorer - add more data columns to the Service Provider section: ([#3474])
|
|
||||||
- network-requester: support report if they run an open proxy using `ControlRequest` API ([#3461])
|
|
||||||
- Refactor client configs (London discussion) ([#3444])
|
|
||||||
- Increase `DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE` to 2000 to improve reliability ([#3433])
|
|
||||||
- socks5: sender waits for lanes to clear even though the connection is closed ([#3366])
|
|
||||||
- version bump for variables ([#3545])
|
|
||||||
|
|
||||||
[#3534]: https://github.com/nymtech/nym/issues/3534
|
|
||||||
[#3474]: https://github.com/nymtech/nym/issues/3474
|
|
||||||
[#3461]: https://github.com/nymtech/nym/issues/3461
|
|
||||||
[#3444]: https://github.com/nymtech/nym/issues/3444
|
|
||||||
[#3433]: https://github.com/nymtech/nym/issues/3433
|
|
||||||
[#3366]: https://github.com/nymtech/nym/issues/3366
|
|
||||||
[#3545]: https://github.com/nymtech/nym/pull/3545
|
|
||||||
|
|
||||||
## [v1.1.20] (2023-06-06)
|
|
||||||
|
|
||||||
- Explorer - Fix SP supported apps list ([#3458])
|
|
||||||
- Investigate if we need to lower `SHUTDOWN_TIMEOUT` in socks5 to zero (or almost zero) ([#3438])
|
|
||||||
- Explorer - show all gateways in the default view regardless of the version number ([#3427])
|
|
||||||
- service-provider-directory: add signature check when announcing ([#3360])
|
|
||||||
- Support functionality for nym-name-service (nym-api, nym-cli, etc) ([#3355])
|
|
||||||
- Edit the nym-network-requester to support the enabled-credentials-mode flag ([#3101])
|
|
||||||
- [BUG] network requester documentation update ([#3493])
|
|
||||||
- removing hardcoded version numbers ([#3485])
|
|
||||||
- [BUG] network requester documentation update ([#3481])
|
|
||||||
- [BUG] network requester documentation update ([#3469])
|
|
||||||
- Sign when announcing service providers to the directory contract ([#3459])
|
|
||||||
- mixnode documentation update ([#3435])
|
|
||||||
- updated readme with new developer chat links + new docs links ([#3141])
|
|
||||||
|
|
||||||
[#3458]: https://github.com/nymtech/nym/issues/3458
|
|
||||||
[#3438]: https://github.com/nymtech/nym/issues/3438
|
|
||||||
[#3427]: https://github.com/nymtech/nym/issues/3427
|
|
||||||
[#3360]: https://github.com/nymtech/nym/issues/3360
|
|
||||||
[#3355]: https://github.com/nymtech/nym/issues/3355
|
|
||||||
[#3101]: https://github.com/nymtech/nym/issues/3101
|
|
||||||
[#3493]: https://github.com/nymtech/nym/pull/3493
|
|
||||||
[#3485]: https://github.com/nymtech/nym/pull/3485
|
|
||||||
[#3481]: https://github.com/nymtech/nym/pull/3481
|
|
||||||
[#3469]: https://github.com/nymtech/nym/pull/3469
|
|
||||||
[#3459]: https://github.com/nymtech/nym/pull/3459
|
|
||||||
[#3435]: https://github.com/nymtech/nym/pull/3435
|
|
||||||
[#3141]: https://github.com/nymtech/nym/pull/3141
|
|
||||||
|
|
||||||
## [v1.1.19] (2023-05-16)
|
|
||||||
|
|
||||||
- nym-name-service endpoint in nym-api ([#3403])
|
|
||||||
- Implement key storage for WASM client using IndexedDB (for browser) ([#3329])
|
|
||||||
- Initial version of nym-name-service contract providing name aliases for nym-addresses ([#3274])
|
|
||||||
- Update Cargo.lock ([#3410])
|
|
||||||
|
|
||||||
[#3403]: https://github.com/nymtech/nym/issues/3403
|
|
||||||
[#3329]: https://github.com/nymtech/nym/issues/3329
|
|
||||||
[#3274]: https://github.com/nymtech/nym/issues/3274
|
|
||||||
[#3410]: https://github.com/nymtech/nym/pull/3410
|
|
||||||
|
|
||||||
## [v1.1.18] (2023-05-09)
|
|
||||||
|
|
||||||
- Implement heartbeat messages between socks5 proxy and network requester ([#3215])
|
|
||||||
|
|
||||||
[#3215]: https://github.com/nymtech/nym/issues/3215
|
|
||||||
|
|
||||||
## [v1.1.17] (2023-05-02)
|
|
||||||
|
|
||||||
- Add service-provider-directory-contract support to nym-cli ([#3334])
|
|
||||||
- Start using the node-testing-utils (implemented in #3270) in nym-api Network monitor to simplify the logic there ([#3312])
|
|
||||||
- Add service-provider-directory support to validator-client ([#3296])
|
|
||||||
- Allow topology injection in our WASM client ('test my node' feature) ([#3270])
|
|
||||||
- Expose service-provider-directory contract data in nym-api endpoints ([#3242])
|
|
||||||
- Cache service provider contract in nym-api ([#3241])
|
|
||||||
- Feature/1 1 17 docs ([#3370])
|
|
||||||
- adding a test for SP endpoint ([#3367])
|
|
||||||
- Feature/store cipher ([#3350])
|
|
||||||
|
|
||||||
[#3334]: https://github.com/nymtech/nym/issues/3334
|
|
||||||
[#3312]: https://github.com/nymtech/nym/issues/3312
|
|
||||||
[#3296]: https://github.com/nymtech/nym/issues/3296
|
|
||||||
[#3270]: https://github.com/nymtech/nym/issues/3270
|
|
||||||
[#3242]: https://github.com/nymtech/nym/issues/3242
|
|
||||||
[#3241]: https://github.com/nymtech/nym/issues/3241
|
|
||||||
[#3370]: https://github.com/nymtech/nym/pull/3370
|
|
||||||
[#3367]: https://github.com/nymtech/nym/pull/3367
|
|
||||||
[#3350]: https://github.com/nymtech/nym/pull/3350
|
|
||||||
|
|
||||||
## [v1.1.16] (2023-04-25)
|
## [v1.1.16] (2023-04-25)
|
||||||
|
|
||||||
- Explorer - Fix sorting function on Stake Saturation. It is currently working per page and not globally ([#3320])
|
- Explorer - Fix sorting function on Stake Saturation. It is currently working per page and not globally ([#3320])
|
||||||
|
|||||||
Generated
+550
-1456
File diff suppressed because it is too large
Load Diff
+1
-11
@@ -37,7 +37,6 @@ members = [
|
|||||||
"common/cosmwasm-smart-contracts/group-contract",
|
"common/cosmwasm-smart-contracts/group-contract",
|
||||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||||
"common/cosmwasm-smart-contracts/multisig-contract",
|
"common/cosmwasm-smart-contracts/multisig-contract",
|
||||||
"common/cosmwasm-smart-contracts/name-service",
|
|
||||||
"common/cosmwasm-smart-contracts/service-provider-directory",
|
"common/cosmwasm-smart-contracts/service-provider-directory",
|
||||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||||
"common/credential-storage",
|
"common/credential-storage",
|
||||||
@@ -61,14 +60,12 @@ members = [
|
|||||||
"common/nymsphinx/forwarding",
|
"common/nymsphinx/forwarding",
|
||||||
"common/nymsphinx/framing",
|
"common/nymsphinx/framing",
|
||||||
"common/nymsphinx/params",
|
"common/nymsphinx/params",
|
||||||
"common/nymsphinx/routing",
|
|
||||||
"common/nymsphinx/types",
|
"common/nymsphinx/types",
|
||||||
"common/pemstore",
|
"common/pemstore",
|
||||||
"common/socks5-client-core",
|
"common/socks5-client-core",
|
||||||
"common/socks5/proxy-helpers",
|
"common/socks5/proxy-helpers",
|
||||||
"common/socks5/requests",
|
"common/socks5/requests",
|
||||||
"common/statistics",
|
"common/statistics",
|
||||||
"common/store-cipher",
|
|
||||||
"common/task",
|
"common/task",
|
||||||
"common/topology",
|
"common/topology",
|
||||||
"common/types",
|
"common/types",
|
||||||
@@ -78,7 +75,6 @@ members = [
|
|||||||
"gateway/gateway-requests",
|
"gateway/gateway-requests",
|
||||||
"integrations/bity",
|
"integrations/bity",
|
||||||
"mixnode",
|
"mixnode",
|
||||||
"sdk/lib/socks5-listener",
|
|
||||||
"sdk/rust/nym-sdk",
|
"sdk/rust/nym-sdk",
|
||||||
"service-providers/common",
|
"service-providers/common",
|
||||||
"service-providers/network-requester",
|
"service-providers/network-requester",
|
||||||
@@ -87,7 +83,6 @@ members = [
|
|||||||
"nym-api/nym-api-requests",
|
"nym-api/nym-api-requests",
|
||||||
"nym-outfox",
|
"nym-outfox",
|
||||||
"tools/nym-cli",
|
"tools/nym-cli",
|
||||||
"tools/nym-nr-query",
|
|
||||||
"tools/ts-rs-cli"
|
"tools/ts-rs-cli"
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -113,7 +108,6 @@ edition = "2021"
|
|||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
anyhow = "1.0.71"
|
|
||||||
async-trait = "0.1.64"
|
async-trait = "0.1.64"
|
||||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
@@ -121,16 +115,13 @@ cosmwasm-derive = "=1.0.0"
|
|||||||
cosmwasm-schema = "=1.0.0"
|
cosmwasm-schema = "=1.0.0"
|
||||||
cosmwasm-std = "=1.0.0"
|
cosmwasm-std = "=1.0.0"
|
||||||
cosmwasm-storage = "=1.0.0"
|
cosmwasm-storage = "=1.0.0"
|
||||||
cw-controllers = "=0.13.4"
|
|
||||||
cw-storage-plus = "=0.13.4"
|
|
||||||
cw-utils = "=0.13.4"
|
cw-utils = "=0.13.4"
|
||||||
|
cw-storage-plus = "=0.13.4"
|
||||||
cw2 = { version = "=0.13.4" }
|
cw2 = { version = "=0.13.4" }
|
||||||
cw3 = { version = "=0.13.4" }
|
cw3 = { version = "=0.13.4" }
|
||||||
cw3-fixed-multisig = { version = "=0.13.4" }
|
cw3-fixed-multisig = { version = "=0.13.4" }
|
||||||
cw4 = { version = "=0.13.4" }
|
cw4 = { version = "=0.13.4" }
|
||||||
dotenvy = "0.15.6"
|
dotenvy = "0.15.6"
|
||||||
generic-array = "0.14.7"
|
|
||||||
getrandom = "0.2.10"
|
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
once_cell = "1.7.2"
|
once_cell = "1.7.2"
|
||||||
@@ -141,4 +132,3 @@ tap = "1.0.1"
|
|||||||
thiserror = "1.0.38"
|
thiserror = "1.0.38"
|
||||||
tokio = "1.24.1"
|
tokio = "1.24.1"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
zeroize = "1.6.0"
|
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
|
|||||||
VESTING_CONTRACT=$(CONTRACTS_OUT_DIR)/vesting_contract.wasm
|
VESTING_CONTRACT=$(CONTRACTS_OUT_DIR)/vesting_contract.wasm
|
||||||
MIXNET_CONTRACT=$(CONTRACTS_OUT_DIR)/mixnet_contract.wasm
|
MIXNET_CONTRACT=$(CONTRACTS_OUT_DIR)/mixnet_contract.wasm
|
||||||
SERVICE_PROVIDER_DIRECTORY_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_service_provider_directory.wasm
|
SERVICE_PROVIDER_DIRECTORY_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_service_provider_directory.wasm
|
||||||
NAME_SERVICE_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_name_service.wasm
|
|
||||||
|
|
||||||
wasm: wasm-build wasm-opt
|
wasm: wasm-build wasm-opt
|
||||||
|
|
||||||
@@ -110,7 +109,6 @@ wasm-opt:
|
|||||||
wasm-opt --disable-sign-ext -Os $(VESTING_CONTRACT) -o $(VESTING_CONTRACT)
|
wasm-opt --disable-sign-ext -Os $(VESTING_CONTRACT) -o $(VESTING_CONTRACT)
|
||||||
wasm-opt --disable-sign-ext -Os $(MIXNET_CONTRACT) -o $(MIXNET_CONTRACT)
|
wasm-opt --disable-sign-ext -Os $(MIXNET_CONTRACT) -o $(MIXNET_CONTRACT)
|
||||||
wasm-opt --disable-sign-ext -Os $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) -o $(SERVICE_PROVIDER_DIRECTORY_CONTRACT)
|
wasm-opt --disable-sign-ext -Os $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) -o $(SERVICE_PROVIDER_DIRECTORY_CONTRACT)
|
||||||
wasm-opt --disable-sign-ext -Os $(NAME_SERVICE_CONTRACT) -o $(NAME_SERVICE_CONTRACT)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Misc
|
# Misc
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
|||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/build-nym.html).
|
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/building-nym.html).
|
||||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html).
|
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
|
||||||
|
|
||||||
### Developing
|
### Developing
|
||||||
|
|
||||||
@@ -32,11 +32,7 @@ For Typescript components, please see [ts-packages](./ts-packages).
|
|||||||
|
|
||||||
### Developer chat
|
### Developer chat
|
||||||
|
|
||||||
> We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team.
|
You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel.
|
||||||
|
|
||||||
You can chat to us in two places:
|
|
||||||
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
|
|
||||||
* The various developer channels on [Discord](https://discord.gg/nym)
|
|
||||||
|
|
||||||
### Rewards
|
### Rewards
|
||||||
|
|
||||||
@@ -50,7 +46,7 @@ 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=\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=\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=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">|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=\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 competitivness 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=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=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 NYMT.
|
|<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.
|
||||||
@@ -74,7 +70,7 @@ Operator of node `i` is credited with the following amount:
|
|||||||
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
|
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=\color{white}min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
|
<img src="https://render.githubusercontent.com/render/math?math=\color{white}min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
|
||||||
|
|
||||||
Delegate with stake `s` receives:
|
Delegate with stake `s` recieves:
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
|
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=\color{white}max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
|
<img src="https://render.githubusercontent.com/render/math?math=\color{white}max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ serde = { workspace = true, features = ["derive"] }
|
|||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
||||||
|
|
||||||
|
|
||||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||||
nym-client-core = { path = "../../common/client-core" }
|
|
||||||
nym-config = { path = "../../common/config" }
|
nym-config = { path = "../../common/config" }
|
||||||
nym-credentials = { path = "../../common/credentials" }
|
nym-credentials = { path = "../../common/credentials" }
|
||||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
@@ -9,14 +9,13 @@ use commands::*;
|
|||||||
use error::Result;
|
use error::Result;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bin_common::completions::fig_generate;
|
use nym_bin_common::completions::fig_generate;
|
||||||
use nym_config::DEFAULT_DATA_DIR;
|
use nym_config::{CRED_DB_FILE_NAME, DATA_DIR};
|
||||||
use nym_network_defaults::{setup_env, NymNetworkDetails};
|
use nym_network_defaults::{setup_env, NymNetworkDetails};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser};
|
||||||
use nym_bin_common::logging::setup_logging;
|
use nym_bin_common::logging::setup_logging;
|
||||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
|
||||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||||
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
|
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
|
||||||
use nym_validator_client::Config;
|
use nym_validator_client::Config;
|
||||||
@@ -72,11 +71,10 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Command::Run(r) => {
|
Command::Run(r) => {
|
||||||
// we assume the structure of <home-dir>/data
|
let db_path = r
|
||||||
let data_dir = r.client_home_directory.join(DEFAULT_DATA_DIR);
|
.client_home_directory
|
||||||
let paths = CommonClientPaths::new_default(data_dir);
|
.join(DATA_DIR)
|
||||||
let db_path = paths.credentials_database;
|
.join(CRED_DB_FILE_NAME);
|
||||||
|
|
||||||
let shared_storage =
|
let shared_storage =
|
||||||
nym_credential_storage::initialise_persistent_storage(db_path).await;
|
nym_credential_storage::initialise_persistent_storage(db_path).await;
|
||||||
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.22"
|
version = "1.1.16"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
description = "Implementation of the Nym Client"
|
description = "Implementation of the Nym Client"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,104 +1,99 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::persistence::ClientPaths;
|
use crate::client::config::template::config_template;
|
||||||
use crate::client::config::template::CONFIG_TEMPLATE;
|
use nym_client_core::config::ClientCoreConfigTrait;
|
||||||
use nym_bin_common::logging::LoggingSettings;
|
|
||||||
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||||
use nym_config::{
|
use nym_config::{NymConfig, OptionalSet};
|
||||||
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
|
|
||||||
OptionalSet, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::io;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub use nym_client_core::config::Config as BaseClientConfig;
|
pub use nym_client_core::config::Config as BaseConfig;
|
||||||
|
pub use nym_client_core::config::MISSING_VALUE;
|
||||||
pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
|
pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
|
||||||
|
|
||||||
pub mod old_config_v1_1_13;
|
pub mod old_config_v1_1_13;
|
||||||
pub mod old_config_v1_1_20;
|
|
||||||
pub mod old_config_v1_1_20_2;
|
|
||||||
mod persistence;
|
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
const DEFAULT_CLIENTS_DIR: &str = "clients";
|
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
/// Derive default path to clients's config directory.
|
pub enum SocketType {
|
||||||
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config`
|
WebSocket,
|
||||||
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
None,
|
||||||
must_get_home()
|
|
||||||
.join(NYM_DIR)
|
|
||||||
.join(DEFAULT_CLIENTS_DIR)
|
|
||||||
.join(id)
|
|
||||||
.join(DEFAULT_CONFIG_DIR)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive default path to client's config file.
|
impl SocketType {
|
||||||
/// It should get resolved to `$HOME/.nym/clients/<id>/config/config.toml`
|
pub fn from_string<S: Into<String>>(val: S) -> Self {
|
||||||
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
|
let mut upper = val.into();
|
||||||
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
|
upper.make_ascii_uppercase();
|
||||||
|
match upper.as_ref() {
|
||||||
|
"WEBSOCKET" | "WS" => SocketType::WebSocket,
|
||||||
|
_ => SocketType::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_websocket(&self) -> bool {
|
||||||
|
matches!(self, SocketType::WebSocket)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive default path to client's data directory where files, such as keys, are stored.
|
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
/// It should get resolved to `$HOME/.nym/clients/<id>/data`
|
#[serde(deny_unknown_fields)]
|
||||||
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
|
||||||
must_get_home()
|
|
||||||
.join(NYM_DIR)
|
|
||||||
.join(DEFAULT_CLIENTS_DIR)
|
|
||||||
.join(id)
|
|
||||||
.join(DEFAULT_DATA_DIR)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: BaseClientConfig,
|
base: BaseConfig<Config>,
|
||||||
|
|
||||||
pub socket: Socket,
|
socket: Socket,
|
||||||
|
|
||||||
// pub paths: CommonClientPathfinder,
|
|
||||||
pub storage_paths: ClientPaths,
|
|
||||||
|
|
||||||
pub logging: LoggingSettings,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NymConfigTemplate for Config {
|
impl NymConfig for Config {
|
||||||
fn template() -> &'static str {
|
fn template() -> &'static str {
|
||||||
CONFIG_TEMPLATE
|
config_template()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_root_directory() -> PathBuf {
|
||||||
|
dirs::home_dir()
|
||||||
|
.expect("Failed to evaluate $HOME value")
|
||||||
|
.join(".nym")
|
||||||
|
.join("clients")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_default_root_directory() -> Option<PathBuf> {
|
||||||
|
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_directory(&self) -> PathBuf {
|
||||||
|
self.base.get_nym_root_directory()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory()
|
||||||
|
.join(self.base.get_id())
|
||||||
|
.join("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory().join(self.base.get_id()).join("data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientCoreConfigTrait for Config {
|
||||||
|
fn get_gateway_endpoint(&self) -> &nym_client_core::config::GatewayEndpointConfig {
|
||||||
|
self.base.get_gateway_endpoint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
pub fn new<S: Into<String>>(id: S) -> Self {
|
||||||
Config {
|
Config {
|
||||||
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
|
base: BaseConfig::new(id),
|
||||||
storage_paths: ClientPaths::new_default(default_data_directory(id.as_ref())),
|
|
||||||
logging: Default::default(),
|
|
||||||
socket: Default::default(),
|
socket: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_location(&self) -> PathBuf {
|
|
||||||
default_config_filepath(&self.base.client.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save_to_default_location(&self) -> io::Result<()> {
|
|
||||||
let config_save_location: PathBuf = self.default_location();
|
|
||||||
save_formatted_config_to_file(self, config_save_location)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate(&self) -> bool {
|
pub fn validate(&self) -> bool {
|
||||||
// no other sections have explicit requirements (yet)
|
// no other sections have explicit requirements (yet)
|
||||||
self.base.validate()
|
self.base.validate()
|
||||||
@@ -128,10 +123,39 @@ impl Config {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getters
|
||||||
|
pub fn get_config_file_save_location(&self) -> PathBuf {
|
||||||
|
self.config_directory().join(Self::config_file_name())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_base(&self) -> &BaseConfig<Self> {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_base_mut(&mut self) -> &mut BaseConfig<Self> {
|
||||||
|
&mut self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_debug_settings(&self) -> &DebugConfig {
|
||||||
|
self.get_base().get_debug_config()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_socket_type(&self) -> SocketType {
|
||||||
|
self.socket.socket_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_listening_ip(&self) -> IpAddr {
|
||||||
|
self.socket.host
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_listening_port(&self) -> u16 {
|
||||||
|
self.socket.listening_port
|
||||||
|
}
|
||||||
|
|
||||||
// poor man's 'builder' method
|
// poor man's 'builder' method
|
||||||
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
|
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||||
{
|
{
|
||||||
self.base = f(self.base, val);
|
self.base = f(self.base, val);
|
||||||
self
|
self
|
||||||
@@ -141,7 +165,7 @@ impl Config {
|
|||||||
// (plz, lets refactor it)
|
// (plz, lets refactor it)
|
||||||
pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
|
pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||||
{
|
{
|
||||||
self.base = self.base.with_optional(f, val);
|
self.base = self.base.with_optional(f, val);
|
||||||
self
|
self
|
||||||
@@ -149,7 +173,7 @@ impl Config {
|
|||||||
|
|
||||||
pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
|
pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||||
T: FromStr,
|
T: FromStr,
|
||||||
<T as FromStr>::Err: Debug,
|
<T as FromStr>::Err: Debug,
|
||||||
{
|
{
|
||||||
@@ -165,7 +189,7 @@ impl Config {
|
|||||||
parser: G,
|
parser: G,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||||
G: Fn(&str) -> T,
|
G: Fn(&str) -> T,
|
||||||
{
|
{
|
||||||
self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
|
self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
|
||||||
@@ -173,34 +197,19 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// define_optional_set_inner!(Config, base, BaseClientConfig);
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub enum SocketType {
|
|
||||||
WebSocket,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SocketType {
|
|
||||||
pub fn is_websocket(&self) -> bool {
|
|
||||||
matches!(self, SocketType::WebSocket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(default, deny_unknown_fields)]
|
#[serde(default, deny_unknown_fields)]
|
||||||
pub struct Socket {
|
pub struct Socket {
|
||||||
pub socket_type: SocketType,
|
socket_type: SocketType,
|
||||||
pub host: IpAddr,
|
host: IpAddr,
|
||||||
pub listening_port: u16,
|
listening_port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Socket {
|
impl Default for Socket {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Socket {
|
Socket {
|
||||||
socket_type: SocketType::WebSocket,
|
socket_type: SocketType::WebSocket,
|
||||||
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
|
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
|
||||||
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
|
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,58 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::old_config_v1_1_20::{ConfigV1_1_20, SocketV1_1_20};
|
use crate::client::config::{Config, Socket};
|
||||||
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
|
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
use nym_config::NymConfig;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct OldConfigV1_1_13 {
|
pub struct OldConfigV1_1_13 {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
|
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
|
||||||
|
|
||||||
pub socket: SocketV1_1_20,
|
socket: Socket,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MigrationNymConfig for OldConfigV1_1_13 {
|
impl NymConfig for OldConfigV1_1_13 {
|
||||||
|
fn template() -> &'static str {
|
||||||
|
// not intended to be used
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
fn default_root_directory() -> PathBuf {
|
fn default_root_directory() -> PathBuf {
|
||||||
dirs::home_dir()
|
dirs::home_dir()
|
||||||
.expect("Failed to evaluate $HOME value")
|
.expect("Failed to evaluate $HOME value")
|
||||||
.join(".nym")
|
.join(".nym")
|
||||||
.join("clients")
|
.join("clients")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_default_root_directory() -> Option<PathBuf> {
|
||||||
|
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_directory(&self) -> PathBuf {
|
||||||
|
self.base.client.nym_root_directory.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory()
|
||||||
|
.join(&self.base.client.id)
|
||||||
|
.join("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory()
|
||||||
|
.join(&self.base.client.id)
|
||||||
|
.join("data")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OldConfigV1_1_13> for ConfigV1_1_20 {
|
impl From<OldConfigV1_1_13> for Config {
|
||||||
fn from(value: OldConfigV1_1_13) -> Self {
|
fn from(value: OldConfigV1_1_13) -> Self {
|
||||||
ConfigV1_1_20 {
|
Config {
|
||||||
base: value.base.into(),
|
base: value.base.into(),
|
||||||
socket: value.socket,
|
socket: value.socket,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::client::config::old_config_v1_1_20_2::{
|
|
||||||
ClientPathsV1_1_20_2, ConfigV1_1_20_2, SocketTypeV1_1_20_2, SocketV1_1_20_2,
|
|
||||||
};
|
|
||||||
use nym_bin_common::logging::LoggingSettings;
|
|
||||||
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
|
|
||||||
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::ConfigV1_1_20 as BaseConfigV1_1_20;
|
|
||||||
use nym_client_core::config::old_config_v1_1_20_2::{
|
|
||||||
ClientV1_1_20_2, ConfigV1_1_20_2 as BaseConfigV1_1_20_2,
|
|
||||||
};
|
|
||||||
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub enum SocketTypeV1_1_20 {
|
|
||||||
WebSocket,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SocketTypeV1_1_20> for SocketTypeV1_1_20_2 {
|
|
||||||
fn from(value: SocketTypeV1_1_20) -> Self {
|
|
||||||
match value {
|
|
||||||
SocketTypeV1_1_20::WebSocket => SocketTypeV1_1_20_2::WebSocket,
|
|
||||||
SocketTypeV1_1_20::None => SocketTypeV1_1_20_2::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct ConfigV1_1_20 {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: BaseConfigV1_1_20<ConfigV1_1_20>,
|
|
||||||
|
|
||||||
pub socket: SocketV1_1_20,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
|
|
||||||
fn from(value: ConfigV1_1_20) -> Self {
|
|
||||||
ConfigV1_1_20_2 {
|
|
||||||
base: BaseConfigV1_1_20_2 {
|
|
||||||
client: ClientV1_1_20_2 {
|
|
||||||
version: value.base.client.version,
|
|
||||||
id: value.base.client.id,
|
|
||||||
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
|
|
||||||
nyxd_urls: value.base.client.nyxd_urls,
|
|
||||||
nym_api_urls: value.base.client.nym_api_urls,
|
|
||||||
gateway_endpoint: value.base.client.gateway_endpoint.into(),
|
|
||||||
},
|
|
||||||
debug: value.base.debug.into(),
|
|
||||||
},
|
|
||||||
socket: value.socket.into(),
|
|
||||||
storage_paths: ClientPathsV1_1_20_2 {
|
|
||||||
common_paths: CommonClientPathsV1_1_20_2 {
|
|
||||||
keys: ClientKeysPaths {
|
|
||||||
private_identity_key_file: value.base.client.private_identity_key_file,
|
|
||||||
public_identity_key_file: value.base.client.public_identity_key_file,
|
|
||||||
private_encryption_key_file: value.base.client.private_encryption_key_file,
|
|
||||||
public_encryption_key_file: value.base.client.public_encryption_key_file,
|
|
||||||
gateway_shared_key_file: value.base.client.gateway_shared_key_file,
|
|
||||||
ack_key_file: value.base.client.ack_key_file,
|
|
||||||
},
|
|
||||||
credentials_database: value.base.client.database_path,
|
|
||||||
reply_surb_database: value.base.client.reply_surb_database_path,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
logging: LoggingSettings::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MigrationNymConfig for ConfigV1_1_20 {
|
|
||||||
fn default_root_directory() -> PathBuf {
|
|
||||||
dirs::home_dir()
|
|
||||||
.expect("Failed to evaluate $HOME value")
|
|
||||||
.join(".nym")
|
|
||||||
.join("clients")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct SocketV1_1_20 {
|
|
||||||
socket_type: SocketTypeV1_1_20,
|
|
||||||
host: IpAddr,
|
|
||||||
listening_port: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SocketV1_1_20> for SocketV1_1_20_2 {
|
|
||||||
fn from(value: SocketV1_1_20) -> Self {
|
|
||||||
SocketV1_1_20_2 {
|
|
||||||
socket_type: value.socket_type.into(),
|
|
||||||
host: value.host,
|
|
||||||
listening_port: value.listening_port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SocketV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
SocketV1_1_20 {
|
|
||||||
socket_type: SocketTypeV1_1_20::WebSocket,
|
|
||||||
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
|
|
||||||
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::client::config::persistence::ClientPaths;
|
|
||||||
use crate::client::config::{default_config_filepath, Config, Socket, SocketType};
|
|
||||||
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::GatewayEndpointConfig;
|
|
||||||
use nym_config::read_config_from_toml_file;
|
|
||||||
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::io;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
|
||||||
pub struct ClientPathsV1_1_20_2 {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub common_paths: CommonClientPathsV1_1_20_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
|
||||||
pub struct ConfigV1_1_20_2 {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: BaseConfigV1_1_20_2,
|
|
||||||
|
|
||||||
pub socket: SocketV1_1_20_2,
|
|
||||||
|
|
||||||
pub storage_paths: ClientPathsV1_1_20_2,
|
|
||||||
|
|
||||||
pub logging: LoggingSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV1_1_20_2 {
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
|
||||||
// so its returned to be stored elsewhere.
|
|
||||||
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
|
|
||||||
let gateway_details = self.base.client.gateway_endpoint.clone().into();
|
|
||||||
let config = Config {
|
|
||||||
base: self.base.into(),
|
|
||||||
socket: self.socket.into(),
|
|
||||||
storage_paths: ClientPaths {
|
|
||||||
common_paths: self.storage_paths.common_paths.upgrade_default(),
|
|
||||||
},
|
|
||||||
logging: self.logging,
|
|
||||||
};
|
|
||||||
|
|
||||||
(config, gateway_details)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub enum SocketTypeV1_1_20_2 {
|
|
||||||
WebSocket,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SocketTypeV1_1_20_2> for SocketType {
|
|
||||||
fn from(value: SocketTypeV1_1_20_2) -> Self {
|
|
||||||
match value {
|
|
||||||
SocketTypeV1_1_20_2::WebSocket => SocketType::WebSocket,
|
|
||||||
SocketTypeV1_1_20_2::None => SocketType::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct SocketV1_1_20_2 {
|
|
||||||
pub socket_type: SocketTypeV1_1_20_2,
|
|
||||||
pub host: IpAddr,
|
|
||||||
pub listening_port: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SocketV1_1_20_2> for Socket {
|
|
||||||
fn from(value: SocketV1_1_20_2) -> Self {
|
|
||||||
Socket {
|
|
||||||
socket_type: value.socket_type.into(),
|
|
||||||
host: value.host,
|
|
||||||
listening_port: value.listening_port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SocketV1_1_20_2 {
|
|
||||||
fn default() -> Self {
|
|
||||||
SocketV1_1_20_2 {
|
|
||||||
socket_type: SocketTypeV1_1_20_2::WebSocket,
|
|
||||||
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
|
|
||||||
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
|
||||||
pub struct ClientPaths {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub common_paths: CommonClientPaths,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientPaths {
|
|
||||||
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
|
|
||||||
ClientPaths {
|
|
||||||
common_paths: CommonClientPaths::new_default(base_data_directory),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
pub(crate) fn config_template() -> &'static str {
|
||||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
// While using normal toml marshalling would have been way simpler with less overhead,
|
||||||
// particular fields.
|
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||||
// Note: any changes to the template must be reflected in the appropriate structs.
|
// particular fields.
|
||||||
pub(crate) const CONFIG_TEMPLATE: &str = r#"
|
// Note: any changes to the template must be reflected in the appropriate structs.
|
||||||
|
r#"
|
||||||
# This is a TOML config file.
|
# This is a TOML config file.
|
||||||
# For more information, see https://github.com/toml-lang/toml
|
# For more information, see https://github.com/toml-lang/toml
|
||||||
|
|
||||||
@@ -36,37 +37,50 @@ nym_api_urls = [
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
]
|
]
|
||||||
|
|
||||||
[storage_paths]
|
|
||||||
|
|
||||||
# Path to file containing private identity key.
|
# Path to file containing private identity key.
|
||||||
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
|
private_identity_key_file = '{{ client.private_identity_key_file }}'
|
||||||
|
|
||||||
# Path to file containing public identity key.
|
# Path to file containing public identity key.
|
||||||
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
|
public_identity_key_file = '{{ client.public_identity_key_file }}'
|
||||||
|
|
||||||
# Path to file containing private encryption key.
|
# Path to file containing private encryption key.
|
||||||
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}'
|
private_encryption_key_file = '{{ client.private_encryption_key_file }}'
|
||||||
|
|
||||||
# Path to file containing public encryption key.
|
# Path to file containing public encryption key.
|
||||||
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}'
|
public_encryption_key_file = '{{ client.public_encryption_key_file }}'
|
||||||
|
|
||||||
|
# Path to the database containing bandwidth credentials
|
||||||
|
database_path = '{{ client.database_path }}'
|
||||||
|
|
||||||
|
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
||||||
|
reply_surb_database_path = '{{ client.reply_surb_database_path }}'
|
||||||
|
|
||||||
|
##### additional client config options #####
|
||||||
|
|
||||||
# A gateway specific, optional, base58 stringified shared key used for
|
# A gateway specific, optional, base58 stringified shared key used for
|
||||||
# communication with particular gateway.
|
# communication with particular gateway.
|
||||||
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}'
|
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||||
|
|
||||||
# Path to file containing key used for encrypting and decrypting the content of an
|
# Path to file containing key used for encrypting and decrypting the content of an
|
||||||
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
||||||
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
|
ack_key_file = '{{ client.ack_key_file }}'
|
||||||
|
|
||||||
|
##### advanced configuration options #####
|
||||||
|
|
||||||
# Path to the database containing bandwidth credentials
|
# Absolute path to the home Nym Clients directory.
|
||||||
credentials_database = '{{ storage_paths.credentials_database }}'
|
nym_root_directory = '{{ client.nym_root_directory }}'
|
||||||
|
|
||||||
|
[client.gateway_endpoint]
|
||||||
|
# ID of the gateway from which the client should be fetching messages.
|
||||||
|
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
|
||||||
|
|
||||||
|
# Address of the gateway owner to which the client should send messages.
|
||||||
|
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
|
||||||
|
|
||||||
|
# Address of the gateway listener to which all client requests should be sent.
|
||||||
|
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
|
||||||
|
|
||||||
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
|
||||||
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
|
|
||||||
|
|
||||||
# Path to the file containing information about gateway used by this client,
|
|
||||||
# i.e. details such as its public key, owner address or the network information.
|
|
||||||
gateway_details = '{{ storage_paths.gateway_details }}'
|
|
||||||
|
|
||||||
##### socket config options #####
|
##### socket config options #####
|
||||||
|
|
||||||
@@ -106,5 +120,5 @@ average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
|
|||||||
[debug.cover_traffic]
|
[debug.cover_traffic]
|
||||||
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
||||||
|
|
||||||
|
"#
|
||||||
"#;
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::Config;
|
use crate::client::config::Config;
|
||||||
@@ -6,40 +6,86 @@ use crate::error::ClientError;
|
|||||||
use crate::websocket;
|
use crate::websocket;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
|
use nym_bandwidth_controller::BandwidthController;
|
||||||
use nym_client_core::client::base_client::storage::OnDiskPersistent;
|
|
||||||
use nym_client_core::client::base_client::{
|
use nym_client_core::client::base_client::{
|
||||||
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||||
};
|
};
|
||||||
use nym_client_core::client::inbound_messages::InputMessage;
|
use nym_client_core::client::inbound_messages::InputMessage;
|
||||||
use nym_client_core::client::received_buffer::{
|
use nym_client_core::client::received_buffer::{
|
||||||
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
|
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
|
||||||
};
|
};
|
||||||
|
use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||||
use nym_sphinx::params::PacketType;
|
use nym_sphinx::params::PacketType;
|
||||||
use nym_task::connections::TransmissionLane;
|
use nym_task::connections::TransmissionLane;
|
||||||
use nym_task::TaskManager;
|
use nym_task::TaskManager;
|
||||||
use nym_validator_client::nyxd::QueryNyxdClient;
|
use nym_validator_client::nyxd::QueryNyxdClient;
|
||||||
use nym_validator_client::Client;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use tokio::sync::watch::error::SendError;
|
use tokio::sync::watch::error::SendError;
|
||||||
|
|
||||||
|
pub use nym_client_core::client::key_manager::KeyManager;
|
||||||
|
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||||
pub use nym_sphinx::addressing::clients::Recipient;
|
pub use nym_sphinx::addressing::clients::Recipient;
|
||||||
pub use nym_sphinx::receiver::ReconstructedMessage;
|
pub use nym_sphinx::receiver::ReconstructedMessage;
|
||||||
|
use nym_validator_client::Client;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
type NativeClientBuilder<'a> = BaseClientBuilder<'a, Client<QueryNyxdClient>, OnDiskPersistent>;
|
|
||||||
|
|
||||||
pub struct SocketClient {
|
pub struct SocketClient {
|
||||||
/// Client configuration options, including, among other things, packet sending rates,
|
/// Client configuration options, including, among other things, packet sending rates,
|
||||||
/// key filepaths, etc.
|
/// key filepaths, etc.
|
||||||
config: Config,
|
config: Config,
|
||||||
|
|
||||||
|
/// KeyManager object containing smart pointers to all relevant keys used by the client.
|
||||||
|
key_manager: KeyManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SocketClient {
|
impl SocketClient {
|
||||||
pub fn new(config: Config) -> Self {
|
pub fn new(config: Config) -> Self {
|
||||||
SocketClient { config }
|
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||||
|
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
|
||||||
|
|
||||||
|
SocketClient {
|
||||||
|
config,
|
||||||
|
key_manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_keys(config: Config, key_manager: KeyManager) -> Self {
|
||||||
|
SocketClient {
|
||||||
|
config,
|
||||||
|
key_manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_bandwidth_controller(
|
||||||
|
config: &Config,
|
||||||
|
) -> BandwidthController<Client<QueryNyxdClient>, PersistentStorage> {
|
||||||
|
let details = nym_network_defaults::NymNetworkDetails::new_from_env();
|
||||||
|
let mut client_config =
|
||||||
|
nym_validator_client::Config::try_from_nym_network_details(&details)
|
||||||
|
.expect("failed to construct validator client config");
|
||||||
|
let nyxd_url = config
|
||||||
|
.get_base()
|
||||||
|
.get_validator_endpoints()
|
||||||
|
.pop()
|
||||||
|
.expect("No nyxd validator endpoint provided");
|
||||||
|
let api_url = config
|
||||||
|
.get_base()
|
||||||
|
.get_nym_api_endpoints()
|
||||||
|
.pop()
|
||||||
|
.expect("No validator api endpoint provided");
|
||||||
|
// overwrite env configuration with config URLs
|
||||||
|
client_config = client_config.with_urls(nyxd_url, api_url);
|
||||||
|
let client = nym_validator_client::Client::new_query(client_config)
|
||||||
|
.expect("Could not construct query client");
|
||||||
|
BandwidthController::new(
|
||||||
|
nym_credential_storage::initialise_persistent_storage(
|
||||||
|
config.get_base().get_database_path(),
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
client,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_websocket_listener(
|
fn start_websocket_listener(
|
||||||
@@ -49,7 +95,6 @@ impl SocketClient {
|
|||||||
client_state: ClientState,
|
client_state: ClientState,
|
||||||
self_address: &Recipient,
|
self_address: &Recipient,
|
||||||
shutdown: nym_task::TaskClient,
|
shutdown: nym_task::TaskClient,
|
||||||
packet_type: PacketType,
|
|
||||||
) {
|
) {
|
||||||
info!("Starting websocket listener...");
|
info!("Starting websocket listener...");
|
||||||
|
|
||||||
@@ -75,10 +120,10 @@ impl SocketClient {
|
|||||||
self_address,
|
self_address,
|
||||||
shared_lane_queue_lengths,
|
shared_lane_queue_lengths,
|
||||||
reply_controller_sender,
|
reply_controller_sender,
|
||||||
Some(packet_type),
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
websocket::Listener::new(config.socket.host, config.socket.listening_port)
|
websocket::Listener::new(config.get_listening_ip(), config.get_listening_port())
|
||||||
.start(websocket_handler, shutdown);
|
.start(websocket_handler, shutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,39 +136,31 @@ impl SocketClient {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn initialise_storage(&self) -> Result<OnDiskPersistent, ClientError> {
|
|
||||||
Ok(OnDiskPersistent::from_paths(
|
|
||||||
self.config.storage_paths.common_paths.clone(),
|
|
||||||
&self.config.base.debug,
|
|
||||||
)
|
|
||||||
.await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: see if this could also be shared with socks5 client / nym-sdk maybe
|
|
||||||
async fn create_base_client_builder(&self) -> Result<NativeClientBuilder, ClientError> {
|
|
||||||
// don't create dkg client for the bandwidth controller if credentials are disabled
|
|
||||||
let dkg_query_client = if self.config.base.client.disabled_credentials_mode {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(default_query_dkg_client_from_config(&self.config.base))
|
|
||||||
};
|
|
||||||
|
|
||||||
let storage = self.initialise_storage().await?;
|
|
||||||
|
|
||||||
let base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client);
|
|
||||||
|
|
||||||
Ok(base_client)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
|
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
|
||||||
if !self.config.socket.socket_type.is_websocket() {
|
if !self.config.get_socket_type().is_websocket() {
|
||||||
return Err(ClientError::InvalidSocketMode);
|
return Err(ClientError::InvalidSocketMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
let base_builder = self.create_base_client_builder().await?;
|
// don't create bandwidth controller if credentials are disabled
|
||||||
let packet_type = self.config.base.debug.traffic.packet_type;
|
let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Self::create_bandwidth_controller(&self.config).await)
|
||||||
|
};
|
||||||
|
|
||||||
|
let base_builder = BaseClientBuilder::new_from_base_config(
|
||||||
|
self.config.get_base(),
|
||||||
|
self.key_manager,
|
||||||
|
bandwidth_controller,
|
||||||
|
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||||
|
Some(self.config.get_base().get_reply_surb_database_path()),
|
||||||
|
self.config.get_debug_settings(),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let self_address = base_builder.as_mix_recipient();
|
||||||
let mut started_client = base_builder.start_base().await?;
|
let mut started_client = base_builder.start_base().await?;
|
||||||
let self_address = started_client.address;
|
|
||||||
let client_input = started_client.client_input.register_producer();
|
let client_input = started_client.client_input.register_producer();
|
||||||
let client_output = started_client.client_output.register_consumer();
|
let client_output = started_client.client_output.register_consumer();
|
||||||
let client_state = started_client.client_state;
|
let client_state = started_client.client_state;
|
||||||
@@ -135,24 +172,43 @@ impl SocketClient {
|
|||||||
client_state,
|
client_state,
|
||||||
&self_address,
|
&self_address,
|
||||||
started_client.task_manager.subscribe(),
|
started_client.task_manager.subscribe(),
|
||||||
packet_type,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
info!("Client startup finished!");
|
info!("Client startup finished!");
|
||||||
info!("The address of this client is: {self_address}");
|
info!("The address of this client is: {}", self_address);
|
||||||
|
|
||||||
Ok(started_client.task_manager)
|
Ok(started_client.task_manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
|
pub async fn start_direct(
|
||||||
if self.config.socket.socket_type.is_websocket() {
|
self,
|
||||||
|
packet_type: Option<PacketType>,
|
||||||
|
) -> Result<DirectClient, ClientError> {
|
||||||
|
if self.config.get_socket_type().is_websocket() {
|
||||||
return Err(ClientError::InvalidSocketMode);
|
return Err(ClientError::InvalidSocketMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
let base_builder = self.create_base_client_builder().await?;
|
// don't create bandwidth controller if credentials are disabled
|
||||||
let packet_type = self.config.base.debug.traffic.packet_type;
|
let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
|
||||||
let mut started_client = base_builder.start_base().await?;
|
None
|
||||||
let address = started_client.address;
|
} else {
|
||||||
|
Some(Self::create_bandwidth_controller(&self.config).await)
|
||||||
|
};
|
||||||
|
|
||||||
|
let base_client = BaseClientBuilder::new_from_base_config(
|
||||||
|
self.config.get_base(),
|
||||||
|
self.key_manager,
|
||||||
|
bandwidth_controller,
|
||||||
|
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||||
|
Some(self.config.get_base().get_reply_surb_database_path()),
|
||||||
|
self.config.get_debug_settings(),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let address = base_client.as_mix_recipient();
|
||||||
|
|
||||||
|
let mut started_client = base_client.start_base().await?;
|
||||||
let client_input = started_client.client_input.register_producer();
|
let client_input = started_client.client_input.register_producer();
|
||||||
let client_output = started_client.client_output.register_consumer();
|
let client_output = started_client.client_output.register_consumer();
|
||||||
|
|
||||||
@@ -187,7 +243,7 @@ pub struct DirectClient {
|
|||||||
|
|
||||||
// we need to keep reference to this guy otherwise things will start dropping
|
// we need to keep reference to this guy otherwise things will start dropping
|
||||||
shutdown_notifier: TaskManager,
|
shutdown_notifier: TaskManager,
|
||||||
packet_type: PacketType,
|
packet_type: Option<PacketType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectClient {
|
impl DirectClient {
|
||||||
@@ -208,7 +264,7 @@ impl DirectClient {
|
|||||||
/// well enough in local tests)
|
/// well enough in local tests)
|
||||||
pub async fn send_regular_message(&mut self, recipient: Recipient, message: Vec<u8>) {
|
pub async fn send_regular_message(&mut self, recipient: Recipient, message: Vec<u8>) {
|
||||||
let lane = TransmissionLane::General;
|
let lane = TransmissionLane::General;
|
||||||
let input_msg = InputMessage::new_regular(recipient, message, lane, Some(self.packet_type));
|
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
|
||||||
|
|
||||||
self.client_input
|
self.client_input
|
||||||
.input_sender
|
.input_sender
|
||||||
@@ -227,13 +283,8 @@ impl DirectClient {
|
|||||||
reply_surbs: u32,
|
reply_surbs: u32,
|
||||||
) {
|
) {
|
||||||
let lane = TransmissionLane::General;
|
let lane = TransmissionLane::General;
|
||||||
let input_msg = InputMessage::new_anonymous(
|
let input_msg =
|
||||||
recipient,
|
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
|
||||||
message,
|
|
||||||
reply_surbs,
|
|
||||||
lane,
|
|
||||||
Some(self.packet_type),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.client_input
|
self.client_input
|
||||||
.input_sender
|
.input_sender
|
||||||
@@ -247,8 +298,7 @@ impl DirectClient {
|
|||||||
/// well enough in local tests)
|
/// well enough in local tests)
|
||||||
pub async fn send_reply(&mut self, recipient_tag: AnonymousSenderTag, message: Vec<u8>) {
|
pub async fn send_reply(&mut self, recipient_tag: AnonymousSenderTag, message: Vec<u8>) {
|
||||||
let lane = TransmissionLane::General;
|
let lane = TransmissionLane::General;
|
||||||
let input_msg =
|
let input_msg = InputMessage::new_reply(recipient_tag, message, lane, self.packet_type);
|
||||||
InputMessage::new_reply(recipient_tag, message, lane, Some(self.packet_type));
|
|
||||||
|
|
||||||
self.client_input
|
self.client_input
|
||||||
.input_sender
|
.input_sender
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::{
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
default_config_directory, default_config_filepath, default_data_directory,
|
|
||||||
};
|
|
||||||
use crate::commands::try_upgrade_config;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::config::Config,
|
client::config::Config,
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
@@ -12,16 +9,13 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use nym_bin_common::output_format::OutputFormat;
|
use nym_bin_common::output_format::OutputFormat;
|
||||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
use nym_config::NymConfig;
|
||||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||||
use nym_client_core::config::GatewayEndpointConfig;
|
|
||||||
use nym_client_core::init::GatewaySetup;
|
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::{fs, io};
|
|
||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
#[derive(Args, Clone)]
|
||||||
@@ -103,15 +97,15 @@ impl From<Init> for OverrideConfig {
|
|||||||
pub struct InitResults {
|
pub struct InitResults {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
client_core: nym_client_core::init::InitResults,
|
client_core: nym_client_core::init::InitResults,
|
||||||
client_listening_port: u16,
|
client_listening_port: String,
|
||||||
client_address: String,
|
client_address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitResults {
|
impl InitResults {
|
||||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
fn new(config: &Config, address: &Recipient) -> Self {
|
||||||
Self {
|
Self {
|
||||||
client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway),
|
client_core: nym_client_core::init::InitResults::new(config.get_base(), address),
|
||||||
client_listening_port: config.socket.listening_port,
|
client_listening_port: config.get_listening_port().to_string(),
|
||||||
client_address: address.to_string(),
|
client_address: address.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,26 +119,18 @@ impl Display for InitResults {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_paths(id: &str) -> io::Result<()> {
|
|
||||||
fs::create_dir_all(default_data_directory(id))?;
|
|
||||||
fs::create_dir_all(default_config_directory(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||||
eprintln!("Initialising client...");
|
eprintln!("Initialising client...");
|
||||||
|
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let already_init = if default_config_filepath(id).exists() {
|
let already_init = Config::default_config_file_path(id).exists();
|
||||||
|
if already_init {
|
||||||
// in case we're using old config, try to upgrade it
|
// in case we're using old config, try to upgrade it
|
||||||
// (if we're using the current version, it's a no-op)
|
// (if we're using the current version, it's a no-op)
|
||||||
try_upgrade_config(id)?;
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
eprintln!("Client \"{id}\" was already initialised before");
|
eprintln!("Client \"{id}\" was already initialised before");
|
||||||
true
|
}
|
||||||
} else {
|
|
||||||
init_paths(id)?;
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Usually you only register with the gateway on the first init, however you can force
|
// Usually you only register with the gateway on the first init, however you can force
|
||||||
// re-registering if wanted.
|
// re-registering if wanted.
|
||||||
@@ -160,44 +146,45 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
|||||||
|
|
||||||
// Attempt to use a user-provided gateway, if possible
|
// Attempt to use a user-provided gateway, if possible
|
||||||
let user_chosen_gateway_id = args.gateway;
|
let user_chosen_gateway_id = args.gateway;
|
||||||
let gateway_setup = GatewaySetup::new_fresh(
|
|
||||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
|
||||||
Some(args.latency_based_selection),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load and potentially override config
|
// Load and potentially override config
|
||||||
let config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
||||||
|
|
||||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||||
// one but with keys kept, or reusing the gateway configuration.
|
// one but with keys kept, or reusing the gateway configuration.
|
||||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, PersistentStorage>(
|
||||||
let details_store =
|
|
||||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
|
||||||
let init_details = nym_client_core::init::setup_gateway(
|
|
||||||
&gateway_setup,
|
|
||||||
&key_store,
|
|
||||||
&details_store,
|
|
||||||
register_gateway,
|
register_gateway,
|
||||||
Some(&config.base.client.nym_api_urls),
|
user_chosen_gateway_id,
|
||||||
|
config.get_base(),
|
||||||
|
args.latency_based_selection,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||||
|
|
||||||
let config_save_location = config.default_location();
|
config.get_base_mut().set_gateway_endpoint(gateway);
|
||||||
config.save_to_default_location().tap_err(|_| {
|
|
||||||
|
config.save_to_file(None).tap_err(|_| {
|
||||||
log::error!("Failed to save the config file");
|
log::error!("Failed to save the config file");
|
||||||
})?;
|
})?;
|
||||||
eprintln!(
|
|
||||||
"Saved configuration file to {}",
|
|
||||||
config_save_location.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
let address = init_details.client_address()?;
|
print_saved_config(&config);
|
||||||
|
|
||||||
eprintln!("Client configuration completed.\n");
|
let address = nym_client_core::init::get_client_address_from_stored_keys(config.get_base())?;
|
||||||
|
let init_results = InitResults::new(&config, &address);
|
||||||
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
|
|
||||||
println!("{}", args.output.format(&init_results));
|
println!("{}", args.output.format(&init_results));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_saved_config(config: &Config) {
|
||||||
|
let config_save_location = config.get_config_file_save_location();
|
||||||
|
eprintln!("Saved configuration file to {config_save_location:?}");
|
||||||
|
eprintln!("Using gateway: {}", config.get_base().get_gateway_id());
|
||||||
|
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
|
||||||
|
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
|
||||||
|
log::debug!(
|
||||||
|
"Gateway listener: {}",
|
||||||
|
config.get_base().get_gateway_listener()
|
||||||
|
);
|
||||||
|
eprintln!("Client configuration completed.\n");
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,23 +2,14 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
|
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||||
use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
|
use crate::client::config::{BaseConfig, Config};
|
||||||
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
|
|
||||||
use crate::client::config::{BaseClientConfig, Config};
|
|
||||||
use crate::error::ClientError;
|
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::{error, info};
|
use log::info;
|
||||||
use nym_bin_common::build_information::BinaryBuildInformation;
|
use nym_bin_common::build_information::BinaryBuildInformation;
|
||||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
use nym_config::{NymConfig, OptionalSet};
|
||||||
OnDiskGatewayDetails, PersistedGatewayDetails,
|
|
||||||
};
|
|
||||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
|
||||||
use nym_client_core::config::GatewayEndpointConfig;
|
|
||||||
use nym_client_core::error::ClientCoreError;
|
|
||||||
use nym_config::OptionalSet;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
@@ -91,151 +82,40 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
|
|||||||
pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||||
config
|
config
|
||||||
.with_optional(Config::with_disabled_socket, args.disable_socket)
|
.with_optional(Config::with_disabled_socket, args.disable_socket)
|
||||||
.with_base(
|
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
|
||||||
BaseClientConfig::with_high_default_traffic_volume,
|
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
|
||||||
args.fastmode,
|
|
||||||
)
|
|
||||||
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
|
|
||||||
.with_optional(Config::with_port, args.port)
|
.with_optional(Config::with_port, args.port)
|
||||||
.with_optional(Config::with_host, args.host)
|
.with_optional(Config::with_host, args.host)
|
||||||
.with_optional_custom_env_ext(
|
.with_optional_custom_env_ext(
|
||||||
BaseClientConfig::with_custom_nym_apis,
|
BaseConfig::with_custom_nym_apis,
|
||||||
args.nym_apis,
|
args.nym_apis,
|
||||||
nym_network_defaults::var_names::NYM_API,
|
nym_network_defaults::var_names::NYM_API,
|
||||||
nym_config::parse_urls,
|
nym_config::parse_urls,
|
||||||
)
|
)
|
||||||
.with_optional_custom_env_ext(
|
.with_optional_custom_env_ext(
|
||||||
BaseClientConfig::with_custom_nyxd,
|
BaseConfig::with_custom_nyxd,
|
||||||
args.nyxd_urls,
|
args.nyxd_urls,
|
||||||
nym_network_defaults::var_names::NYXD,
|
nym_network_defaults::var_names::NYXD,
|
||||||
nym_config::parse_urls,
|
nym_config::parse_urls,
|
||||||
)
|
)
|
||||||
.with_optional_ext(
|
.with_optional_ext(
|
||||||
BaseClientConfig::with_disabled_credentials,
|
BaseConfig::with_disabled_credentials,
|
||||||
args.enabled_credentials_mode.map(|b| !b),
|
args.enabled_credentials_mode.map(|b| !b),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn persist_gateway_details(
|
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
|
||||||
config: &Config,
|
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
|
||||||
details: GatewayEndpointConfig,
|
|
||||||
) -> Result<(), ClientError> {
|
|
||||||
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| {
|
|
||||||
ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
|
|
||||||
source: Box::new(source),
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
|
|
||||||
details_store
|
|
||||||
.store_to_disk(&persisted_details)
|
|
||||||
.map_err(|source| {
|
|
||||||
ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
|
|
||||||
source: Box::new(source),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
|
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
|
||||||
|
|
||||||
// explicitly load it as v1.1.13 (which is incompatible with the next step, i.e. 1.1.19)
|
|
||||||
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
|
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
|
||||||
// if we failed to load it, there might have been nothing to upgrade
|
// 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
|
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||||
return Ok(false);
|
return Ok(());
|
||||||
};
|
};
|
||||||
info!("It seems the client is using <= v1.1.13 config template.");
|
info!("It seems the client is using <= v1.1.13 config template.");
|
||||||
info!("It is going to get updated to the current specification.");
|
info!("It is going to get updated to the current specification.");
|
||||||
|
|
||||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
let updated: Config = old_config.into();
|
||||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
updated.save_to_file(None)
|
||||||
let (updated, gateway_config) = updated_step2.upgrade();
|
|
||||||
persist_gateway_details(&updated, gateway_config)?;
|
|
||||||
|
|
||||||
updated.save_to_default_location()?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
|
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
|
||||||
|
|
||||||
// explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
|
|
||||||
let Ok(old_config) = ConfigV1_1_20::load_from_file(id) else {
|
|
||||||
// if we failed to load it, there might have been nothing to upgrade
|
|
||||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
|
||||||
return Ok(false);
|
|
||||||
};
|
|
||||||
info!("It seems the client is using <= v1.1.20 config template.");
|
|
||||||
info!("It is going to get updated to the current specification.");
|
|
||||||
|
|
||||||
let updated_step1: ConfigV1_1_20_2 = old_config.into();
|
|
||||||
let (updated, gateway_config) = updated_step1.upgrade();
|
|
||||||
persist_gateway_details(&updated, gateway_config)?;
|
|
||||||
|
|
||||||
updated.save_to_default_location()?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
|
|
||||||
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
|
|
||||||
let Ok(old_config) = ConfigV1_1_20_2::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.20_2 config template.");
|
|
||||||
info!("It is going to get updated to the current specification.");
|
|
||||||
|
|
||||||
let (updated, gateway_config) = old_config.upgrade();
|
|
||||||
persist_gateway_details(&updated, gateway_config)?;
|
|
||||||
|
|
||||||
updated.save_to_default_location()?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
|
|
||||||
if try_upgrade_v1_1_13_config(id)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if try_upgrade_v1_1_20_config(id)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if try_upgrade_v1_1_20_2_config(id)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
|
|
||||||
// try to load the config as is
|
|
||||||
if let Ok(cfg) = Config::read_from_default_path(id) {
|
|
||||||
return if !cfg.validate() {
|
|
||||||
Err(ClientError::ConfigValidationFailure)
|
|
||||||
} else {
|
|
||||||
Ok(cfg)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// we couldn't load it - try upgrading it from older revisions
|
|
||||||
try_upgrade_config(id)?;
|
|
||||||
|
|
||||||
let config = match Config::read_from_default_path(id) {
|
|
||||||
Ok(cfg) => cfg,
|
|
||||||
Err(err) => {
|
|
||||||
error!("Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})");
|
|
||||||
return Err(ClientError::FailedToLoadConfig(id.to_string()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !config.validate() {
|
|
||||||
return Err(ClientError::ConfigValidationFailure);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::commands::try_load_current_config;
|
use std::error::Error;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{config::Config, SocketClient},
|
client::{config::Config, SocketClient},
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
@@ -10,9 +13,8 @@ use crate::{
|
|||||||
use clap::Args;
|
use clap::Args;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||||
|
use nym_config::NymConfig;
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use std::error::Error;
|
|
||||||
use std::net::IpAddr;
|
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
#[derive(Args, Clone)]
|
||||||
pub(crate) struct Run {
|
pub(crate) struct Run {
|
||||||
@@ -80,7 +82,7 @@ impl From<Run> for OverrideConfig {
|
|||||||
// network version. It might do so in the future.
|
// network version. It might do so in the future.
|
||||||
fn version_check(cfg: &Config) -> bool {
|
fn version_check(cfg: &Config) -> bool {
|
||||||
let binary_version = env!("CARGO_PKG_VERSION");
|
let binary_version = env!("CARGO_PKG_VERSION");
|
||||||
let config_version = &cfg.base.client.version;
|
let config_version = cfg.get_base().get_version();
|
||||||
if binary_version == config_version {
|
if binary_version == config_version {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@@ -96,10 +98,30 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
eprintln!("Starting client {}...", args.id);
|
let id = &args.id;
|
||||||
|
|
||||||
let mut config = try_load_current_config(&args.id)?;
|
// in case we're using old config, try to upgrade it
|
||||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
// (if we're using the current version, it's a no-op)
|
||||||
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
|
|
||||||
|
let mut config = match Config::load_from_file(id) {
|
||||||
|
Ok(cfg) => cfg,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
|
||||||
|
return Err(Box::new(ClientError::FailedToLoadConfig(id.to_string())));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !config.validate() {
|
||||||
|
return Err(Box::new(ClientError::ConfigValidationFailure));
|
||||||
|
}
|
||||||
|
|
||||||
|
let override_config_fields = OverrideConfig::from(args.clone());
|
||||||
|
config = override_config(config, override_config_fields);
|
||||||
|
|
||||||
|
if config.get_base_mut().set_empty_fields_to_defaults() {
|
||||||
|
warn!("some of the core config options were left unset. the default values are going to get used instead.");
|
||||||
|
}
|
||||||
|
|
||||||
if !version_check(&config) {
|
if !version_check(&config) {
|
||||||
error!("failed the local version check");
|
error!("failed the local version check");
|
||||||
|
|||||||
@@ -1,14 +1,42 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::Config;
|
use crate::client::config::{Config, MISSING_VALUE};
|
||||||
use crate::commands::try_load_current_config;
|
|
||||||
use clap::Args;
|
|
||||||
use nym_bin_common::version_checker::Version;
|
use nym_bin_common::version_checker::Version;
|
||||||
|
use nym_config::NymConfig;
|
||||||
|
|
||||||
|
use clap::Args;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
fn unimplemented_upgrade(current_version: &Version, config_version: &Version) -> ! {
|
#[allow(dead_code)]
|
||||||
eprintln!("Cannot perform upgrade from {config_version} to {current_version} as it hasn't been implemented yet");
|
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
|
||||||
|
print_failed_upgrade(from_version, to_version);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
|
println!("\n==================\nTrying to upgrade client from {from} to {to} ...");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
|
eprintln!("Upgrade from {from} to {to} failed!\n==================\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
|
println!("Upgrade from {from} to {to} was successful!\n==================\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outdated_upgrade(config_version: &Version, package_version: &Version) -> ! {
|
||||||
|
eprintln!(
|
||||||
|
"Cannot perform upgrade from {config_version} to {package_version}. Your version is too old to perform the upgrade.!"
|
||||||
|
);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> ! {
|
||||||
|
eprintln!("Cannot perform upgrade from {config_version} to {current_version}. Please let the developers know about this issue if you expected it to work!");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +48,7 @@ pub(crate) struct Upgrade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_config_version(config: &Config) -> Version {
|
fn parse_config_version(config: &Config) -> Version {
|
||||||
let version = Version::parse(&config.base.client.version).unwrap_or_else(|err| {
|
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to parse client version! - {err}");
|
eprintln!("failed to parse client version! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
@@ -49,14 +77,53 @@ fn parse_package_version() -> Version {
|
|||||||
version
|
version
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_upgrade(config: Config, _args: &Upgrade, package_version: &Version) {
|
fn minor_0_12_upgrade(
|
||||||
let config_version = parse_config_version(&config);
|
mut config: Config,
|
||||||
if &config_version == package_version {
|
_matches: &Upgrade,
|
||||||
println!("You're using the most recent version!");
|
config_version: &Version,
|
||||||
return;
|
package_version: &Version,
|
||||||
}
|
) -> Config {
|
||||||
|
let to_version = if package_version.major == 0 && package_version.minor == 12 {
|
||||||
|
package_version.clone()
|
||||||
|
} else {
|
||||||
|
Version::new(0, 12, 0)
|
||||||
|
};
|
||||||
|
|
||||||
unimplemented_upgrade(package_version, &config_version)
|
print_start_upgrade(config_version, &to_version);
|
||||||
|
|
||||||
|
config
|
||||||
|
.get_base_mut()
|
||||||
|
.set_custom_version(to_version.to_string().as_ref());
|
||||||
|
|
||||||
|
config.save_to_file(None).unwrap_or_else(|err| {
|
||||||
|
eprintln!("failed to overwrite config file! - {err}");
|
||||||
|
print_failed_upgrade(config_version, &to_version);
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
print_successful_upgrade(config_version, to_version);
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) {
|
||||||
|
loop {
|
||||||
|
let config_version = parse_config_version(&config);
|
||||||
|
|
||||||
|
if &config_version == package_version {
|
||||||
|
println!("You're using the most recent version!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
config = match config_version.major {
|
||||||
|
0 => match config_version.minor {
|
||||||
|
9 | 10 => outdated_upgrade(&config_version, package_version),
|
||||||
|
11 => minor_0_12_upgrade(config, args, &config_version, package_version),
|
||||||
|
_ => unsupported_upgrade(&config_version, package_version),
|
||||||
|
},
|
||||||
|
_ => unsupported_upgrade(&config_version, package_version),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn execute(args: &Upgrade) {
|
pub(crate) fn execute(args: &Upgrade) {
|
||||||
@@ -64,15 +131,16 @@ pub(crate) fn execute(args: &Upgrade) {
|
|||||||
|
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let existing_config = try_load_current_config(id).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {err}");
|
eprintln!("failed to load existing config file! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|
||||||
if existing_config.base.client.version.is_empty() {
|
if existing_config.get_base().get_version() == MISSING_VALUE {
|
||||||
eprintln!("the existing configuration file does not seem to contain version number.");
|
eprintln!("the existing configuration file does not seem to contain version number.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// here be upgrade path to 0.9.X and beyond based on version number from config
|
||||||
do_upgrade(existing_config, args, &package_version)
|
do_upgrade(existing_config, args, &package_version)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.22"
|
version = "1.1.16"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,25 +1,20 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::commands::try_upgrade_config;
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
use crate::config::{
|
|
||||||
default_config_directory, default_config_filepath, default_data_directory, Config,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
error::Socks5ClientError,
|
error::Socks5ClientError,
|
||||||
};
|
};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use nym_bin_common::output_format::OutputFormat;
|
use nym_bin_common::output_format::OutputFormat;
|
||||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
use nym_config::NymConfig;
|
||||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||||
use nym_client_core::config::GatewayEndpointConfig;
|
|
||||||
use nym_client_core::init::GatewaySetup;
|
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
|
use nym_socks5_client_core::config::Config;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::{fs, io};
|
|
||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
#[derive(Args, Clone)]
|
||||||
@@ -105,19 +100,15 @@ impl From<Init> for OverrideConfig {
|
|||||||
pub struct InitResults {
|
pub struct InitResults {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
client_core: nym_client_core::init::InitResults,
|
client_core: nym_client_core::init::InitResults,
|
||||||
socks5_listening_port: u16,
|
socks5_listening_port: String,
|
||||||
client_address: String,
|
client_address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitResults {
|
impl InitResults {
|
||||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
fn new(config: &Config, address: &Recipient) -> Self {
|
||||||
Self {
|
Self {
|
||||||
client_core: nym_client_core::init::InitResults::new(
|
client_core: nym_client_core::init::InitResults::new(config.get_base(), address),
|
||||||
&config.core.base,
|
socks5_listening_port: config.get_socks5().get_listening_port().to_string(),
|
||||||
address,
|
|
||||||
gateway,
|
|
||||||
),
|
|
||||||
socks5_listening_port: config.core.socks5.listening_port,
|
|
||||||
client_address: address.to_string(),
|
client_address: address.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,27 +122,19 @@ impl Display for InitResults {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_paths(id: &str) -> io::Result<()> {
|
|
||||||
fs::create_dir_all(default_data_directory(id))?;
|
|
||||||
fs::create_dir_all(default_config_directory(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
||||||
eprintln!("Initialising client...");
|
eprintln!("Initialising client...");
|
||||||
|
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
let provider_address = &args.provider;
|
let provider_address = &args.provider;
|
||||||
|
|
||||||
let already_init = if default_config_filepath(id).exists() {
|
let already_init = Config::default_config_file_path(id).exists();
|
||||||
|
if already_init {
|
||||||
// in case we're using old config, try to upgrade it
|
// in case we're using old config, try to upgrade it
|
||||||
// (if we're using the current version, it's a no-op)
|
// (if we're using the current version, it's a no-op)
|
||||||
try_upgrade_config(id)?;
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
eprintln!("SOCKS5 client \"{id}\" was already initialised before");
|
eprintln!("SOCKS5 client \"{id}\" was already initialised before");
|
||||||
true
|
}
|
||||||
} else {
|
|
||||||
init_paths(id)?;
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Usually you only register with the gateway on the first init, however you can force
|
// Usually you only register with the gateway on the first init, however you can force
|
||||||
// re-registering if wanted.
|
// re-registering if wanted.
|
||||||
@@ -167,47 +150,50 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
|||||||
|
|
||||||
// Attempt to use a user-provided gateway, if possible
|
// Attempt to use a user-provided gateway, if possible
|
||||||
let user_chosen_gateway_id = args.gateway;
|
let user_chosen_gateway_id = args.gateway;
|
||||||
let gateway_setup = GatewaySetup::new_fresh(
|
|
||||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
|
||||||
Some(args.latency_based_selection),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load and potentially override config
|
// Load and potentially override config
|
||||||
let config = override_config(
|
let mut config = override_config(
|
||||||
Config::new(id, &provider_address.to_string()),
|
Config::new(id, &provider_address.to_string()),
|
||||||
OverrideConfig::from(args.clone()),
|
OverrideConfig::from(args.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||||
// one but with keys kept, or reusing the gateway configuration.
|
// one but with keys kept, or reusing the gateway configuration.
|
||||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, PersistentStorage>(
|
||||||
let details_store =
|
|
||||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
|
||||||
let init_details = nym_client_core::init::setup_gateway(
|
|
||||||
&gateway_setup,
|
|
||||||
&key_store,
|
|
||||||
&details_store,
|
|
||||||
register_gateway,
|
register_gateway,
|
||||||
Some(&config.core.base.client.nym_api_urls),
|
user_chosen_gateway_id,
|
||||||
|
config.get_base(),
|
||||||
|
args.latency_based_selection,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||||
|
|
||||||
|
config.get_base_mut().set_gateway_endpoint(gateway);
|
||||||
|
|
||||||
// TODO: ask the service provider we specified for its interface version and set it in the config
|
// TODO: ask the service provider we specified for its interface version and set it in the config
|
||||||
|
|
||||||
let config_save_location = config.default_location();
|
config.save_to_file(None).tap_err(|_| {
|
||||||
config.save_to_default_location().tap_err(|_| {
|
|
||||||
log::error!("Failed to save the config file");
|
log::error!("Failed to save the config file");
|
||||||
})?;
|
})?;
|
||||||
eprintln!(
|
|
||||||
"Saved configuration file to {}",
|
|
||||||
config_save_location.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
let address = init_details.client_address()?;
|
print_saved_config(&config);
|
||||||
|
|
||||||
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
|
let address = nym_client_core::init::get_client_address_from_stored_keys(config.get_base())?;
|
||||||
|
let init_results = InitResults::new(&config, &address);
|
||||||
println!("{}", args.output.format(&init_results));
|
println!("{}", args.output.format(&init_results));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_saved_config(config: &Config) {
|
||||||
|
let config_save_location = config.get_config_file_save_location();
|
||||||
|
eprintln!("Saved configuration file to {:?}", config_save_location);
|
||||||
|
eprintln!("Using gateway: {}", config.get_base().get_gateway_id());
|
||||||
|
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
|
||||||
|
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
|
||||||
|
log::debug!(
|
||||||
|
"Gateway listener: {}",
|
||||||
|
config.get_base().get_gateway_listener()
|
||||||
|
);
|
||||||
|
eprintln!("Client configuration completed.\n");
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
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::{BaseClientConfig, Config};
|
|
||||||
use crate::error::Socks5ClientError;
|
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::{error, info};
|
use log::info;
|
||||||
use nym_bin_common::build_information::BinaryBuildInformation;
|
use nym_bin_common::build_information::BinaryBuildInformation;
|
||||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
use nym_config::{NymConfig, OptionalSet};
|
||||||
OnDiskGatewayDetails, PersistedGatewayDetails,
|
use nym_socks5_client_core::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||||
};
|
use nym_socks5_client_core::config::{BaseConfig, Config};
|
||||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
|
||||||
use nym_client_core::config::GatewayEndpointConfig;
|
|
||||||
use nym_client_core::error::ClientCoreError;
|
|
||||||
use nym_config::OptionalSet;
|
|
||||||
use nym_sphinx::params::PacketType;
|
use nym_sphinx::params::PacketType;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
@@ -97,152 +88,41 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
|||||||
PacketType::Mix
|
PacketType::Mix
|
||||||
};
|
};
|
||||||
config
|
config
|
||||||
.with_base(
|
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
|
||||||
BaseClientConfig::with_high_default_traffic_volume,
|
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
|
||||||
args.fastmode,
|
.with_base(BaseConfig::with_packet_type, packet_type)
|
||||||
)
|
|
||||||
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
|
|
||||||
.with_base(BaseClientConfig::with_packet_type, packet_type)
|
|
||||||
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
||||||
.with_optional(Config::with_port, args.port)
|
.with_optional(Config::with_port, args.port)
|
||||||
.with_optional_base_custom_env(
|
.with_optional_custom_env_ext(
|
||||||
BaseClientConfig::with_custom_nym_apis,
|
BaseConfig::with_custom_nym_apis,
|
||||||
args.nym_apis,
|
args.nym_apis,
|
||||||
nym_network_defaults::var_names::NYM_API,
|
nym_network_defaults::var_names::NYM_API,
|
||||||
nym_config::parse_urls,
|
nym_config::parse_urls,
|
||||||
)
|
)
|
||||||
.with_optional_base_custom_env(
|
.with_optional_custom_env_ext(
|
||||||
BaseClientConfig::with_custom_nyxd,
|
BaseConfig::with_custom_nyxd,
|
||||||
args.nyxd_urls,
|
args.nyxd_urls,
|
||||||
nym_network_defaults::var_names::NYXD,
|
nym_network_defaults::var_names::NYXD,
|
||||||
nym_config::parse_urls,
|
nym_config::parse_urls,
|
||||||
)
|
)
|
||||||
.with_optional_base(
|
.with_optional_ext(
|
||||||
BaseClientConfig::with_disabled_credentials,
|
BaseConfig::with_disabled_credentials,
|
||||||
args.enabled_credentials_mode.map(|b| !b),
|
args.enabled_credentials_mode.map(|b| !b),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn persist_gateway_details(
|
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
|
||||||
config: &Config,
|
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
|
||||||
details: GatewayEndpointConfig,
|
|
||||||
) -> Result<(), Socks5ClientError> {
|
|
||||||
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),
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
|
|
||||||
details_store
|
|
||||||
.store_to_disk(&persisted_details)
|
|
||||||
.map_err(|source| {
|
|
||||||
Socks5ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
|
|
||||||
source: Box::new(source),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
|
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
|
||||||
|
|
||||||
// explicitly load it as v1.1.13 (which is incompatible with the next step, i.e. 1.1.19)
|
|
||||||
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
|
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
|
||||||
// if we failed to load it, there might have been nothing to upgrade
|
// 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
|
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||||
return Ok(false);
|
return Ok(());
|
||||||
};
|
};
|
||||||
info!("It seems the client is using <= v1.1.13 config template.");
|
info!("It seems the client is using <= v1.1.13 config template.");
|
||||||
info!("It is going to get updated to the current specification.");
|
info!("It is going to get updated to the current specification.");
|
||||||
|
|
||||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
let updated: Config = old_config.into();
|
||||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
updated.save_to_file(None)
|
||||||
let (updated, gateway_config) = updated_step2.upgrade();
|
|
||||||
persist_gateway_details(&updated, gateway_config)?;
|
|
||||||
|
|
||||||
updated.save_to_default_location()?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
|
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
|
||||||
|
|
||||||
// explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
|
|
||||||
let Ok(old_config) = ConfigV1_1_20::load_from_file(id) else {
|
|
||||||
// if we failed to load it, there might have been nothing to upgrade
|
|
||||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
|
||||||
return Ok(false);
|
|
||||||
};
|
|
||||||
info!("It seems the client is using <= v1.1.20 config template.");
|
|
||||||
info!("It is going to get updated to the current specification.");
|
|
||||||
|
|
||||||
let updated_step1: ConfigV1_1_20_2 = old_config.into();
|
|
||||||
let (updated, gateway_config) = updated_step1.upgrade();
|
|
||||||
persist_gateway_details(&updated, gateway_config)?;
|
|
||||||
|
|
||||||
updated.save_to_default_location()?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
|
|
||||||
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
|
|
||||||
let Ok(old_config) = ConfigV1_1_20_2::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.20_2 config template.");
|
|
||||||
info!("It is going to get updated to the current specification.");
|
|
||||||
|
|
||||||
let (updated, gateway_config) = old_config.upgrade();
|
|
||||||
persist_gateway_details(&updated, gateway_config)?;
|
|
||||||
|
|
||||||
updated.save_to_default_location()?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
|
|
||||||
if try_upgrade_v1_1_13_config(id)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if try_upgrade_v1_1_20_config(id)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if try_upgrade_v1_1_20_2_config(id)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
|
|
||||||
// try to load the config as is
|
|
||||||
if let Ok(cfg) = Config::read_from_default_path(id) {
|
|
||||||
return if !cfg.validate() {
|
|
||||||
Err(Socks5ClientError::ConfigValidationFailure)
|
|
||||||
} else {
|
|
||||||
Ok(cfg)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// we couldn't load it - try upgrading it from older revisions
|
|
||||||
try_upgrade_config(id)?;
|
|
||||||
|
|
||||||
let config = match Config::read_from_default_path(id) {
|
|
||||||
Ok(cfg) => cfg,
|
|
||||||
Err(err) => {
|
|
||||||
error!("Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})");
|
|
||||||
return Err(Socks5ClientError::FailedToLoadConfig(id.to_string()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !config.validate() {
|
|
||||||
return Err(Socks5ClientError::ConfigValidationFailure);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::commands::try_load_current_config;
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
use crate::config::Config;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
error::Socks5ClientError,
|
error::Socks5ClientError,
|
||||||
@@ -10,9 +9,9 @@ use crate::{
|
|||||||
use clap::Args;
|
use clap::Args;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||||
use nym_client_core::client::base_client::storage::OnDiskPersistent;
|
use nym_config::NymConfig;
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_socks5_client_core::NymClient;
|
use nym_socks5_client_core::{config::Config, NymClient};
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
#[derive(Args, Clone)]
|
||||||
@@ -21,6 +20,10 @@ pub(crate) struct Run {
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
id: String,
|
id: String,
|
||||||
|
|
||||||
|
/// Custom path to the nym-mixnet-client configuration file
|
||||||
|
#[clap(long)]
|
||||||
|
config: Option<String>,
|
||||||
|
|
||||||
/// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
|
/// 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
|
/// While this is going to hide its actual address information, it will make the actual communication
|
||||||
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
|
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
|
||||||
@@ -88,12 +91,13 @@ impl From<Run> for OverrideConfig {
|
|||||||
// network version. It might do so in the future.
|
// network version. It might do so in the future.
|
||||||
fn version_check(cfg: &Config) -> bool {
|
fn version_check(cfg: &Config) -> bool {
|
||||||
let binary_version = env!("CARGO_PKG_VERSION");
|
let binary_version = env!("CARGO_PKG_VERSION");
|
||||||
let config_version = &cfg.core.base.client.version;
|
let config_version = cfg.get_base().get_version();
|
||||||
if binary_version == config_version {
|
if binary_version == config_version {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}",
|
"The mixnode binary has different version than what is specified in config file! {} and {}",
|
||||||
|
binary_version, config_version
|
||||||
);
|
);
|
||||||
if is_minor_version_compatible(binary_version, config_version) {
|
if is_minor_version_compatible(binary_version, config_version) {
|
||||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||||
@@ -106,18 +110,37 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
eprintln!("Starting client {}...", args.id);
|
let id = &args.id;
|
||||||
|
|
||||||
let mut config = try_load_current_config(&args.id)?;
|
// in case we're using old config, try to upgrade it
|
||||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
// (if we're using the current version, it's a no-op)
|
||||||
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
|
|
||||||
|
let mut config = match Config::load_from_file(id) {
|
||||||
|
Ok(cfg) => cfg,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
|
||||||
|
return Err(Box::new(Socks5ClientError::FailedToLoadConfig(
|
||||||
|
id.to_string(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !config.validate() {
|
||||||
|
return Err(Box::new(Socks5ClientError::ConfigValidationFailure));
|
||||||
|
}
|
||||||
|
|
||||||
|
let override_config_fields = OverrideConfig::from(args.clone());
|
||||||
|
config = override_config(config, override_config_fields);
|
||||||
|
|
||||||
|
if config.get_base_mut().set_empty_fields_to_defaults() {
|
||||||
|
warn!("some of the core config options were left unset. the default values are going to get used instead.");
|
||||||
|
}
|
||||||
|
|
||||||
if !version_check(&config) {
|
if !version_check(&config) {
|
||||||
error!("failed the local version check");
|
error!("failed the local version check");
|
||||||
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
|
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
|
||||||
}
|
}
|
||||||
|
|
||||||
let storage =
|
NymClient::new(config).run_forever().await
|
||||||
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
|
|
||||||
.await?;
|
|
||||||
NymClient::new(config.core, storage).run_forever().await
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,50 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::commands::try_load_current_config;
|
|
||||||
use crate::config::Config;
|
|
||||||
use clap::Args;
|
|
||||||
use nym_bin_common::version_checker::Version;
|
use nym_bin_common::version_checker::Version;
|
||||||
use std::process;
|
use nym_config::NymConfig;
|
||||||
|
use nym_socks5_client_core::config::{Config, MISSING_VALUE};
|
||||||
|
|
||||||
fn unimplemented_upgrade(current_version: &Version, config_version: &Version) -> ! {
|
use clap::Args;
|
||||||
eprintln!("Cannot perform upgrade from {config_version} to {current_version} as it hasn't been implemented yet");
|
use std::{fmt::Display, process};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
|
||||||
|
print_failed_upgrade(from_version, to_version);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
|
println!(
|
||||||
|
"\n==================\nTrying to upgrade client from {} to {} ...",
|
||||||
|
from, to
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
|
eprintln!(
|
||||||
|
"Upgrade from {} to {} failed!\n==================\n",
|
||||||
|
from, to
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
|
println!(
|
||||||
|
"Upgrade from {} to {} was successful!\n==================\n",
|
||||||
|
from, to
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outdated_upgrade(config_version: &Version, package_version: &Version) -> ! {
|
||||||
|
eprintln!(
|
||||||
|
"Cannot perform upgrade from {} to {}. Your version is too old to perform the upgrade.!",
|
||||||
|
config_version, package_version
|
||||||
|
);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> ! {
|
||||||
|
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue if you expected it to work!", config_version, current_version);
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,14 +56,15 @@ pub(crate) struct Upgrade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_config_version(config: &Config) -> Version {
|
fn parse_config_version(config: &Config) -> Version {
|
||||||
let version = Version::parse(&config.core.base.client.version).unwrap_or_else(|err| {
|
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to parse client version! - {err}");
|
eprintln!("failed to parse client version! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|
||||||
if version.is_prerelease() || !version.build.is_empty() {
|
if version.is_prerelease() || !version.build.is_empty() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Trying to upgrade from a non-released version {version}. This is not supported!"
|
"Trying to upgrade from a non-released version {}. This is not supported!",
|
||||||
|
version
|
||||||
);
|
);
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
}
|
}
|
||||||
@@ -42,21 +79,63 @@ fn parse_package_version() -> Version {
|
|||||||
// however, we are not using them ourselves at the moment and hence it should be fine.
|
// however, we are not using them ourselves at the moment and hence it should be fine.
|
||||||
// if we change our mind, we could easily tweak this code
|
// if we change our mind, we could easily tweak this code
|
||||||
if version.is_prerelease() || !version.build.is_empty() {
|
if version.is_prerelease() || !version.build.is_empty() {
|
||||||
eprintln!("Trying to upgrade to a non-released version {version}. This is not supported!");
|
eprintln!(
|
||||||
|
"Trying to upgrade to a non-released version {}. This is not supported!",
|
||||||
|
version
|
||||||
|
);
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
version
|
version
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_upgrade(config: Config, _args: &Upgrade, package_version: &Version) {
|
fn minor_0_12_upgrade(
|
||||||
let config_version = parse_config_version(&config);
|
mut config: Config,
|
||||||
if &config_version == package_version {
|
_args: &Upgrade,
|
||||||
println!("You're using the most recent version!");
|
config_version: &Version,
|
||||||
return;
|
package_version: &Version,
|
||||||
}
|
) -> Config {
|
||||||
|
let to_version = if package_version.major == 0 && package_version.minor == 12 {
|
||||||
|
package_version.clone()
|
||||||
|
} else {
|
||||||
|
Version::new(0, 12, 0)
|
||||||
|
};
|
||||||
|
|
||||||
unimplemented_upgrade(package_version, &config_version)
|
print_start_upgrade(config_version, &to_version);
|
||||||
|
|
||||||
|
config
|
||||||
|
.get_base_mut()
|
||||||
|
.set_custom_version(to_version.to_string().as_ref());
|
||||||
|
|
||||||
|
config.save_to_file(None).unwrap_or_else(|err| {
|
||||||
|
eprintln!("failed to overwrite config file! - {err}");
|
||||||
|
print_failed_upgrade(config_version, &to_version);
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
print_successful_upgrade(config_version, to_version);
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) {
|
||||||
|
loop {
|
||||||
|
let config_version = parse_config_version(&config);
|
||||||
|
|
||||||
|
if &config_version == package_version {
|
||||||
|
println!("You're using the most recent version!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
config = match config_version.major {
|
||||||
|
0 => match config_version.minor {
|
||||||
|
9 | 10 => outdated_upgrade(&config_version, package_version),
|
||||||
|
11 => minor_0_12_upgrade(config, args, &config_version, package_version),
|
||||||
|
_ => unsupported_upgrade(&config_version, package_version),
|
||||||
|
},
|
||||||
|
_ => unsupported_upgrade(&config_version, package_version),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn execute(args: &Upgrade) {
|
pub(crate) fn execute(args: &Upgrade) {
|
||||||
@@ -64,15 +143,16 @@ pub(crate) fn execute(args: &Upgrade) {
|
|||||||
|
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let existing_config = try_load_current_config(id).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {err}");
|
eprintln!("failed to load existing config file! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|
||||||
if existing_config.core.base.client.version.is_empty() {
|
if existing_config.get_base().get_version() == MISSING_VALUE {
|
||||||
eprintln!("the existing configuration file does not seem to contain version number.");
|
eprintln!("the existing configuration file does not seem to contain version number.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// here be upgrade path to 0.9.X and beyond based on version number from config
|
||||||
do_upgrade(existing_config, args, &package_version)
|
do_upgrade(existing_config, args, &package_version)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
// 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::{
|
|
||||||
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
|
|
||||||
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::io;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
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;
|
|
||||||
mod persistence;
|
|
||||||
mod template;
|
|
||||||
|
|
||||||
const DEFAULT_SOCKS5_CLIENTS_DIR: &str = "socks5-clients";
|
|
||||||
|
|
||||||
/// Derive default path to clients's config directory.
|
|
||||||
/// It should get resolved to `$HOME/.nym/socks5-clients/<id>/config`
|
|
||||||
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
|
||||||
must_get_home()
|
|
||||||
.join(NYM_DIR)
|
|
||||||
.join(DEFAULT_SOCKS5_CLIENTS_DIR)
|
|
||||||
.join(id)
|
|
||||||
.join(DEFAULT_CONFIG_DIR)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derive default path to client's config file.
|
|
||||||
/// It should get resolved to `$HOME/.nym/socks5-clients/<id>/config/config.toml`
|
|
||||||
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
|
|
||||||
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derive default path to client's data directory where files, such as keys, are stored.
|
|
||||||
/// It should get resolved to `$HOME/.nym/socks5-clients/<id>/data`
|
|
||||||
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
|
||||||
must_get_home()
|
|
||||||
.join(NYM_DIR)
|
|
||||||
.join(DEFAULT_SOCKS5_CLIENTS_DIR)
|
|
||||||
.join(id)
|
|
||||||
.join(DEFAULT_DATA_DIR)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct Config {
|
|
||||||
pub core: CoreConfig,
|
|
||||||
|
|
||||||
pub storage_paths: SocksClientPaths,
|
|
||||||
|
|
||||||
pub logging: LoggingSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NymConfigTemplate for Config {
|
|
||||||
fn template() -> &'static str {
|
|
||||||
CONFIG_TEMPLATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
|
|
||||||
Config {
|
|
||||||
core: CoreConfig::new(
|
|
||||||
id.as_ref(),
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
provider_mix_address.as_ref(),
|
|
||||||
),
|
|
||||||
storage_paths: SocksClientPaths::new_default(default_data_directory(id.as_ref())),
|
|
||||||
logging: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_location(&self) -> PathBuf {
|
|
||||||
default_config_filepath(&self.core.base.client.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save_to_default_location(&self) -> io::Result<()> {
|
|
||||||
let config_save_location: PathBuf = self.default_location();
|
|
||||||
save_formatted_config_to_file(self, config_save_location)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate(&self) -> bool {
|
|
||||||
// no other sections have explicit requirements (yet)
|
|
||||||
self.core.validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_port(mut self, port: u16) -> Self {
|
|
||||||
self.core.socks5.listening_port = port;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_anonymous_replies(mut self, anonymous_replies: bool) -> Self {
|
|
||||||
self.core.socks5.send_anonymously = anonymous_replies;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// poor man's 'builder' method
|
|
||||||
|
|
||||||
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
|
||||||
{
|
|
||||||
self.core = self.core.with_base(f, val);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_optional_base<F, T>(mut self, f: F, val: Option<T>) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
|
||||||
{
|
|
||||||
self.core = self.core.with_optional_base(f, val);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn with_optional_base_env<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
|
||||||
T: FromStr,
|
|
||||||
<T as FromStr>::Err: Debug,
|
|
||||||
{
|
|
||||||
self.core = self.core.with_optional_base_env(f, val, env_var);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_optional_base_custom_env<F, T, G>(
|
|
||||||
mut self,
|
|
||||||
f: F,
|
|
||||||
val: Option<T>,
|
|
||||||
env_var: &str,
|
|
||||||
parser: G,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
|
||||||
G: Fn(&str) -> T,
|
|
||||||
{
|
|
||||||
self.core = self
|
|
||||||
.core
|
|
||||||
.with_optional_base_custom_env(f, val, env_var, parser);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::config::old_config_v1_1_20::{ConfigV1_1_20, Socks5V1_1_20};
|
|
||||||
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
|
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
|
||||||
use nym_config::must_get_home;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct OldConfigV1_1_13 {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
|
|
||||||
|
|
||||||
pub socks5: Socks5V1_1_20,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MigrationNymConfig for OldConfigV1_1_13 {
|
|
||||||
fn default_root_directory() -> PathBuf {
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
let base_dir = must_get_home();
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
||||||
let base_dir = PathBuf::from("/tmp");
|
|
||||||
|
|
||||||
base_dir.join(".nym").join("socks5-clients")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<OldConfigV1_1_13> for ConfigV1_1_20 {
|
|
||||||
fn from(value: OldConfigV1_1_13) -> Self {
|
|
||||||
ConfigV1_1_20 {
|
|
||||||
base: value.base.into(),
|
|
||||||
socks5: value.socks5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::config::old_config_v1_1_20_2::{
|
|
||||||
ConfigV1_1_20_2, CoreConfigV1_1_20_2, SocksClientPathsV1_1_20_2,
|
|
||||||
};
|
|
||||||
use nym_bin_common::logging::LoggingSettings;
|
|
||||||
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
|
|
||||||
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::ConfigV1_1_20 as BaseConfigV1_1_20;
|
|
||||||
use nym_client_core::config::old_config_v1_1_20_2::ClientV1_1_20_2;
|
|
||||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
|
||||||
use nym_config::must_get_home;
|
|
||||||
use nym_socks5_client_core::config::old_config_v1_1_20_2::{
|
|
||||||
BaseClientConfigV1_1_20_2, Socks5DebugV1_1_20_2, Socks5V1_1_20_2,
|
|
||||||
};
|
|
||||||
use nym_socks5_client_core::config::{ProviderInterfaceVersion, Socks5ProtocolVersion};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
|
|
||||||
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct ConfigV1_1_20 {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: BaseConfigV1_1_20<ConfigV1_1_20>,
|
|
||||||
|
|
||||||
pub socks5: Socks5V1_1_20,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
|
|
||||||
fn from(value: ConfigV1_1_20) -> Self {
|
|
||||||
ConfigV1_1_20_2 {
|
|
||||||
core: CoreConfigV1_1_20_2 {
|
|
||||||
base: BaseClientConfigV1_1_20_2 {
|
|
||||||
client: ClientV1_1_20_2 {
|
|
||||||
version: value.base.client.version,
|
|
||||||
id: value.base.client.id,
|
|
||||||
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
|
|
||||||
nyxd_urls: value.base.client.nyxd_urls,
|
|
||||||
nym_api_urls: value.base.client.nym_api_urls,
|
|
||||||
gateway_endpoint: value.base.client.gateway_endpoint.into(),
|
|
||||||
},
|
|
||||||
debug: value.base.debug.into(),
|
|
||||||
},
|
|
||||||
socks5: value.socks5.into(),
|
|
||||||
},
|
|
||||||
storage_paths: SocksClientPathsV1_1_20_2 {
|
|
||||||
common_paths: CommonClientPathsV1_1_20_2 {
|
|
||||||
keys: ClientKeysPaths {
|
|
||||||
private_identity_key_file: value.base.client.private_identity_key_file,
|
|
||||||
public_identity_key_file: value.base.client.public_identity_key_file,
|
|
||||||
private_encryption_key_file: value.base.client.private_encryption_key_file,
|
|
||||||
public_encryption_key_file: value.base.client.public_encryption_key_file,
|
|
||||||
gateway_shared_key_file: value.base.client.gateway_shared_key_file,
|
|
||||||
ack_key_file: value.base.client.ack_key_file,
|
|
||||||
},
|
|
||||||
credentials_database: value.base.client.database_path,
|
|
||||||
reply_surb_database: value.base.client.reply_surb_database_path,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
logging: LoggingSettings::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MigrationNymConfig for ConfigV1_1_20 {
|
|
||||||
fn default_root_directory() -> PathBuf {
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
let base_dir = must_get_home();
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
||||||
let base_dir = PathBuf::from("/tmp");
|
|
||||||
|
|
||||||
base_dir.join(".nym").join("socks5-clients")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct Socks5V1_1_20 {
|
|
||||||
pub listening_port: u16,
|
|
||||||
|
|
||||||
pub provider_mix_address: String,
|
|
||||||
|
|
||||||
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
|
|
||||||
pub provider_interface_version: ProviderInterfaceVersion,
|
|
||||||
|
|
||||||
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
|
|
||||||
pub socks5_protocol_version: Socks5ProtocolVersion,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub send_anonymously: bool,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub socks5_debug: Socks5DebugV1_1_20,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Socks5V1_1_20> for Socks5V1_1_20_2 {
|
|
||||||
fn from(value: Socks5V1_1_20) -> Self {
|
|
||||||
Socks5V1_1_20_2 {
|
|
||||||
listening_port: value.listening_port,
|
|
||||||
provider_mix_address: value.provider_mix_address,
|
|
||||||
provider_interface_version: value.provider_interface_version,
|
|
||||||
socks5_protocol_version: value.socks5_protocol_version,
|
|
||||||
send_anonymously: value.send_anonymously,
|
|
||||||
socks5_debug: value.socks5_debug.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct Socks5DebugV1_1_20 {
|
|
||||||
connection_start_surbs: u32,
|
|
||||||
per_request_surbs: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Socks5DebugV1_1_20> for Socks5DebugV1_1_20_2 {
|
|
||||||
fn from(value: Socks5DebugV1_1_20) -> Self {
|
|
||||||
Socks5DebugV1_1_20_2 {
|
|
||||||
connection_start_surbs: value.connection_start_surbs,
|
|
||||||
per_request_surbs: value.per_request_surbs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Socks5DebugV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
Socks5DebugV1_1_20 {
|
|
||||||
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
|
|
||||||
per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +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_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
|
||||||
use nym_client_core::config::GatewayEndpointConfig;
|
|
||||||
use nym_config::read_config_from_toml_file;
|
|
||||||
pub use nym_socks5_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as CoreConfigV1_1_20_2;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::io;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
|
||||||
pub struct SocksClientPathsV1_1_20_2 {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub common_paths: CommonClientPathsV1_1_20_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct ConfigV1_1_20_2 {
|
|
||||||
pub core: CoreConfigV1_1_20_2,
|
|
||||||
|
|
||||||
pub storage_paths: SocksClientPathsV1_1_20_2,
|
|
||||||
|
|
||||||
pub logging: LoggingSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV1_1_20_2 {
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
|
||||||
// so its returned to be stored elsewhere.
|
|
||||||
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
|
|
||||||
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
|
|
||||||
let config = Config {
|
|
||||||
core: self.core.into(),
|
|
||||||
storage_paths: SocksClientPaths {
|
|
||||||
common_paths: self.storage_paths.common_paths.upgrade_default(),
|
|
||||||
},
|
|
||||||
logging: self.logging,
|
|
||||||
};
|
|
||||||
|
|
||||||
(config, gateway_details)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
|
||||||
pub struct SocksClientPaths {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub common_paths: CommonClientPaths,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SocksClientPaths {
|
|
||||||
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
|
|
||||||
SocksClientPaths {
|
|
||||||
common_paths: CommonClientPaths::new_default(base_data_directory),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
|
||||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
|
||||||
// particular fields.
|
|
||||||
// Note: any changes to the template must be reflected in the appropriate structs.
|
|
||||||
pub(crate) const CONFIG_TEMPLATE: &str = r#"
|
|
||||||
# This is a TOML config file.
|
|
||||||
# For more information, see https://github.com/toml-lang/toml
|
|
||||||
|
|
||||||
##### main base client config options #####
|
|
||||||
|
|
||||||
[core.client]
|
|
||||||
# Version of the client for which this configuration was created.
|
|
||||||
version = '{{ core.client.version }}'
|
|
||||||
|
|
||||||
# Human readable ID of this particular client.
|
|
||||||
id = '{{ core.client.id }}'
|
|
||||||
|
|
||||||
# Indicates whether this client is running in a disabled credentials mode, thus attempting
|
|
||||||
# to claim bandwidth without presenting bandwidth credentials.
|
|
||||||
disabled_credentials_mode = {{ core.client.disabled_credentials_mode }}
|
|
||||||
|
|
||||||
# Addresses to nyxd validators via which the client can communicate with the chain.
|
|
||||||
nyxd_urls = [
|
|
||||||
{{#each core.client.nyxd_urls }}
|
|
||||||
'{{this}}',
|
|
||||||
{{/each}}
|
|
||||||
]
|
|
||||||
|
|
||||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
|
||||||
nym_api_urls = [
|
|
||||||
{{#each core.client.nym_api_urls }}
|
|
||||||
'{{this}}',
|
|
||||||
{{/each}}
|
|
||||||
]
|
|
||||||
|
|
||||||
[storage_paths]
|
|
||||||
|
|
||||||
# Path to file containing private identity key.
|
|
||||||
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
|
|
||||||
|
|
||||||
# Path to file containing public identity key.
|
|
||||||
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
|
|
||||||
|
|
||||||
# Path to file containing private encryption key.
|
|
||||||
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}'
|
|
||||||
|
|
||||||
# Path to file containing public encryption key.
|
|
||||||
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}'
|
|
||||||
|
|
||||||
# A gateway specific, optional, base58 stringified shared key used for
|
|
||||||
# communication with particular gateway.
|
|
||||||
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}'
|
|
||||||
|
|
||||||
# Path to file containing key used for encrypting and decrypting the content of an
|
|
||||||
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
|
||||||
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
|
|
||||||
|
|
||||||
# Path to the database containing bandwidth credentials
|
|
||||||
credentials_database = '{{ storage_paths.credentials_database }}'
|
|
||||||
|
|
||||||
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
|
||||||
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
|
|
||||||
|
|
||||||
# Path to the file containing information about gateway used by this client,
|
|
||||||
# i.e. details such as its public key, owner address or the network information.
|
|
||||||
gateway_details = '{{ storage_paths.gateway_details }}'
|
|
||||||
|
|
||||||
##### socket config options #####
|
|
||||||
|
|
||||||
[core.socks5]
|
|
||||||
|
|
||||||
# The mix address of the provider to which all requests are going to be sent.
|
|
||||||
provider_mix_address = '{{ core.socks5.provider_mix_address }}'
|
|
||||||
|
|
||||||
# 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
|
|
||||||
# slower and consume nearly double the bandwidth as it will require sending reply SURBs.
|
|
||||||
#
|
|
||||||
# Note that some service providers might not support this.
|
|
||||||
send_anonymously = {{ core.socks5.send_anonymously }}
|
|
||||||
|
|
||||||
##### logging configuration options #####
|
|
||||||
|
|
||||||
[logging]
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
|
|
||||||
##### debug configuration options #####
|
|
||||||
# The following options should not be modified unless you know EXACTLY what you are doing
|
|
||||||
# as if set incorrectly, they may impact your anonymity.
|
|
||||||
|
|
||||||
# [core.socks5.socks5_debug]
|
|
||||||
|
|
||||||
|
|
||||||
[core.debug]
|
|
||||||
|
|
||||||
[core.debug.traffic]
|
|
||||||
average_packet_delay = '{{ core.debug.traffic.average_packet_delay }}'
|
|
||||||
message_sending_average_delay = '{{ core.debug.traffic.message_sending_average_delay }}'
|
|
||||||
|
|
||||||
[core.debug.acknowledgements]
|
|
||||||
average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}'
|
|
||||||
|
|
||||||
[core.debug.cover_traffic]
|
|
||||||
loop_cover_traffic_average_delay = '{{ core.debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
|
||||||
|
|
||||||
"#;
|
|
||||||
@@ -8,7 +8,6 @@ use nym_bin_common::logging::{maybe_print_banner, setup_logging};
|
|||||||
use nym_network_defaults::setup_env;
|
use nym_network_defaults::setup_env;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod config;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|||||||
@@ -34,14 +34,7 @@ import {
|
|||||||
StakeSaturationResponse,
|
StakeSaturationResponse,
|
||||||
UnbondedMixnodeResponse,
|
UnbondedMixnodeResponse,
|
||||||
VestingAccountInfo,
|
VestingAccountInfo,
|
||||||
ContractState,
|
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode, DelegationBlock
|
||||||
VestingAccountsCoinPaged,
|
|
||||||
VestingAccountsPaged,
|
|
||||||
DelegationTimes,
|
|
||||||
Delegations,
|
|
||||||
Period,
|
|
||||||
VestingAccountNode,
|
|
||||||
DelegationBlock,
|
|
||||||
} from '@nymproject/types';
|
} from '@nymproject/types';
|
||||||
import QueryClient from './query-client';
|
import QueryClient from './query-client';
|
||||||
import SigningClient, { ISigningClient } from './signing-client';
|
import SigningClient, { ISigningClient } from './signing-client';
|
||||||
@@ -214,7 +207,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
let mixNodes: UnbondedMixnodeResponse[] = [];
|
let mixNodes: UnbondedMixnodeResponse[] = [];
|
||||||
const limit = 50;
|
const limit = 50;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (;;) {
|
for (; ;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
|
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
@@ -237,7 +230,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
let mixNodes: MixNodeBond[] = [];
|
let mixNodes: MixNodeBond[] = [];
|
||||||
const limit = 50;
|
const limit = 50;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (;;) {
|
for (; ;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
|
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
@@ -259,7 +252,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
let mixNodes: MixNodeDetails[] = [];
|
let mixNodes: MixNodeDetails[] = [];
|
||||||
const limit = 50;
|
const limit = 50;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (;;) {
|
for (; ;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
|
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
@@ -291,7 +284,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
let delegations: Delegation[] = [];
|
let delegations: Delegation[] = [];
|
||||||
const limit = 250;
|
const limit = 250;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (;;) {
|
for (; ;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
|
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
@@ -314,7 +307,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
let delegations: Delegation[] = [];
|
let delegations: Delegation[] = [];
|
||||||
const limit = 250;
|
const limit = 250;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (;;) {
|
for (; ;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
|
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
@@ -337,7 +330,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
let delegations: Delegation[] = [];
|
let delegations: Delegation[] = [];
|
||||||
const limit = 250;
|
const limit = 250;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (;;) {
|
for (; ;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
|
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
@@ -525,9 +518,11 @@ export default class ValidatorClient implements INymClient {
|
|||||||
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
|
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// VESTING
|
|
||||||
|
// VESTING
|
||||||
// TODO - MOVE TO A DIFFERENT FILE
|
// TODO - MOVE TO A DIFFERENT FILE
|
||||||
|
|
||||||
|
|
||||||
public async getVestingAccountsPaged(): Promise<VestingAccountsPaged> {
|
public async getVestingAccountsPaged(): Promise<VestingAccountsPaged> {
|
||||||
return this.client.getVestingAccountsPaged(this.vestingContract);
|
return this.client.getVestingAccountsPaged(this.vestingContract);
|
||||||
}
|
}
|
||||||
@@ -613,9 +608,9 @@ export default class ValidatorClient implements INymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getDelegation(address: string, mix_id: number): Promise<DelegationBlock> {
|
public async getDelegation(address: string, mix_id: number): Promise<DelegationBlock> {
|
||||||
return this.client.getDelegation(this.vestingContract, address, mix_id);
|
return this.client.getDelegation(this.vestingContract, address, mix_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getTotalDelegationAmount(address: string, mix_id: number, block_timestamp_sec: number): Promise<Coin> {
|
public async getTotalDelegationAmount(address: string, mix_id: number, block_timestamp_sec: number): Promise<Coin> {
|
||||||
return this.client.getTotalDelegationAmount(this.vestingContract, address, mix_id, block_timestamp_sec);
|
return this.client.getTotalDelegationAmount(this.vestingContract, address, mix_id, block_timestamp_sec);
|
||||||
}
|
}
|
||||||
@@ -623,10 +618,4 @@ export default class ValidatorClient implements INymClient {
|
|||||||
public async getCurrentVestingPeriod(address: string): Promise<Period> {
|
public async getCurrentVestingPeriod(address: string): Promise<Period> {
|
||||||
return this.client.getCurrentVestingPeriod(this.vestingContract, address);
|
return this.client.getCurrentVestingPeriod(this.vestingContract, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SIMULATE
|
|
||||||
|
|
||||||
public async simulateSend(signingAddress: string, from: string, to: string, amount: Coin[]) {
|
|
||||||
return (this.client as SigningClient).simulateSend(signingAddress, from, to, amount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,18 +40,9 @@ import {
|
|||||||
RewardingParams,
|
RewardingParams,
|
||||||
UnbondedMixnodeResponse,
|
UnbondedMixnodeResponse,
|
||||||
VestingAccountInfo,
|
VestingAccountInfo,
|
||||||
ContractState,
|
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode, DelegationBlock
|
||||||
VestingAccountsCoinPaged,
|
|
||||||
VestingAccountsPaged,
|
|
||||||
DelegationTimes,
|
|
||||||
Delegations,
|
|
||||||
Period,
|
|
||||||
VestingAccountNode,
|
|
||||||
DelegationBlock,
|
|
||||||
} from '@nymproject/types';
|
} from '@nymproject/types';
|
||||||
import NymApiQuerier from './nym-api-querier';
|
import NymApiQuerier from './nym-api-querier';
|
||||||
import { makeBankMsgSend } from './utils';
|
|
||||||
import { ISimulateClient } from './types/simulate';
|
|
||||||
|
|
||||||
// methods exposed by `SigningCosmWasmClient`
|
// methods exposed by `SigningCosmWasmClient`
|
||||||
export interface ICosmWasmSigning {
|
export interface ICosmWasmSigning {
|
||||||
@@ -157,7 +148,7 @@ export interface INymSigning {
|
|||||||
clientAddress: string;
|
clientAddress: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSigning, ISimulateClient {
|
export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSigning {
|
||||||
bondMixNode(
|
bondMixNode(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixNode: MixNode,
|
mixNode: MixNode,
|
||||||
@@ -520,11 +511,11 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vesting related
|
// vesting related
|
||||||
|
|
||||||
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
|
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
|
||||||
return this.nyxdQuerier.getVestingAccountsPaged(vestingContractAddress);
|
return this.nyxdQuerier.getVestingAccountsPaged(vestingContractAddress);
|
||||||
}
|
};
|
||||||
|
|
||||||
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
|
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
|
||||||
return this.nyxdQuerier.getVestingAmountsAccountsPaged(vestingContractAddress);
|
return this.nyxdQuerier.getVestingAmountsAccountsPaged(vestingContractAddress);
|
||||||
@@ -578,10 +569,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
return this.nyxdQuerier.getEndTime(vestingContractAddress, vestingAccountAddress);
|
return this.nyxdQuerier.getEndTime(vestingContractAddress, vestingAccountAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getOriginalVestingDetails(
|
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
|
||||||
vestingContractAddress: string,
|
|
||||||
vestingAccountAddress: string,
|
|
||||||
): Promise<OriginalVestingResponse> {
|
|
||||||
return this.nyxdQuerier.getOriginalVestingDetails(vestingContractAddress, vestingAccountAddress);
|
return this.nyxdQuerier.getOriginalVestingDetails(vestingContractAddress, vestingAccountAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,11 +589,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
return this.nyxdQuerier.getGateway(vestingContractAddress, address);
|
return this.nyxdQuerier.getGateway(vestingContractAddress, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegationTimes(
|
getDelegationTimes(vestingContractAddress: string, mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
|
||||||
vestingContractAddress: string,
|
|
||||||
mix_id: number,
|
|
||||||
delegatorAddress: string,
|
|
||||||
): Promise<DelegationTimes> {
|
|
||||||
return this.nyxdQuerier.getDelegationTimes(vestingContractAddress, mix_id, delegatorAddress);
|
return this.nyxdQuerier.getDelegationTimes(vestingContractAddress, mix_id, delegatorAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,38 +597,15 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
return this.nyxdQuerier.getAllDelegations(vestingContractAddress);
|
return this.nyxdQuerier.getAllDelegations(vestingContractAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegation(
|
getDelegation(vestingContractAddress: string, vestingAccountAddress: string, mix_id: number): Promise<DelegationBlock> {
|
||||||
vestingContractAddress: string,
|
|
||||||
vestingAccountAddress: string,
|
|
||||||
mix_id: number,
|
|
||||||
): Promise<DelegationBlock> {
|
|
||||||
return this.nyxdQuerier.getDelegation(vestingContractAddress, vestingAccountAddress, mix_id);
|
return this.nyxdQuerier.getDelegation(vestingContractAddress, vestingAccountAddress, mix_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTotalDelegationAmount(
|
getTotalDelegationAmount(vestingContractAddress: string, vestingAccountAddress: string, mix_id: number, block_timestamp_sec: number): Promise<Coin> {
|
||||||
vestingContractAddress: string,
|
return this.nyxdQuerier.getTotalDelegationAmount(vestingContractAddress, vestingAccountAddress, mix_id, block_timestamp_sec);
|
||||||
vestingAccountAddress: string,
|
|
||||||
mix_id: number,
|
|
||||||
block_timestamp_sec: number,
|
|
||||||
): Promise<Coin> {
|
|
||||||
return this.nyxdQuerier.getTotalDelegationAmount(
|
|
||||||
vestingContractAddress,
|
|
||||||
vestingAccountAddress,
|
|
||||||
mix_id,
|
|
||||||
block_timestamp_sec,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
|
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
|
||||||
return this.nyxdQuerier.getCurrentVestingPeriod(vestingContractAddress, address);
|
return this.nyxdQuerier.getCurrentVestingPeriod(vestingContractAddress, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulation
|
|
||||||
|
|
||||||
// TODO consider adding multipling factor
|
|
||||||
|
|
||||||
simulateSend(signingAddress: string, from: string, to: string, amount: Coin[]) {
|
|
||||||
const sendMsg = makeBankMsgSend(from, to, amount);
|
|
||||||
return this.simulate(signingAddress, [sendMsg], 'simulate send tx');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import expect from 'expect';
|
|
||||||
import ValidatorClient from '../..';
|
|
||||||
|
|
||||||
const dotenv = require('dotenv');
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
// TODO: implement for QA with .env for mnemonics
|
|
||||||
describe('Simualtions', () => {
|
|
||||||
let client: ValidatorClient;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
client = await ValidatorClient.connect(
|
|
||||||
process.env.mnemonic || '',
|
|
||||||
process.env.rpcAddress || '',
|
|
||||||
process.env.validatorAddress || '',
|
|
||||||
process.env.prefix || '',
|
|
||||||
process.env.mixnetContractAddress || '',
|
|
||||||
process.env.vestingContractAddress || '',
|
|
||||||
process.env.denom || '',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can simulate sending tokens', async () => {
|
|
||||||
const res = await client.simulateSend(client.address, client.address, client.address, [
|
|
||||||
{ amount: '400000', denom: 'unym' },
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(typeof res).toBe('number');
|
|
||||||
}).timeout(10000);
|
|
||||||
});
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { Coin } from '@cosmjs/proto-signing';
|
|
||||||
|
|
||||||
export interface ISimulateClient {
|
|
||||||
simulateSend(signingAddress: string, from: string, to: string, amount: Coin[]): Promise<number>;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
bin/
|
bin/
|
||||||
pkg/
|
pkg/
|
||||||
wasm-pack.log
|
wasm-pack.log
|
||||||
|
|||||||
Generated
-5275
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client-wasm"
|
name = "nym-client-wasm"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||||
version = "1.1.1"
|
version = "1.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
@@ -17,7 +17,6 @@ default = ["console_error_panic_hook"]
|
|||||||
offline-test = []
|
offline-test = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.68"
|
|
||||||
bs58 = "0.4.0"
|
bs58 = "0.4.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
@@ -25,13 +24,12 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
serde-wasm-bindgen = "0.5"
|
serde-wasm-bindgen = "0.4"
|
||||||
tokio = { version = "1.24.1", features = ["sync"] }
|
tokio = { version = "1.24.1", features = ["sync"] }
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
|
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
|
||||||
wasm-bindgen-futures = "0.4"
|
wasm-bindgen-futures = "0.4"
|
||||||
thiserror = "1.0.40"
|
thiserror = "1.0.40"
|
||||||
zeroize = "1.6.0"
|
|
||||||
|
|
||||||
wasm-timer = { git = "https://github.com/mmsinclair/wasm-timer", rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"}
|
wasm-timer = { git = "https://github.com/mmsinclair/wasm-timer", rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"}
|
||||||
|
|
||||||
@@ -42,14 +40,13 @@ nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
|||||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||||
nym-credentials = { path = "../../common/credentials" }
|
nym-credentials = { path = "../../common/credentials" }
|
||||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "serde"] }
|
nym-crypto = { path = "../../common/crypto" }
|
||||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||||
nym-sphinx-acknowledgements = { path = "../../common/nymsphinx/acknowledgements", features = ["serde"]}
|
|
||||||
nym-topology = { path = "../../common/topology" }
|
nym-topology = { path = "../../common/topology" }
|
||||||
nym-gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
nym-gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
||||||
nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||||
|
wasm-utils = { path = "../../common/wasm-utils" }
|
||||||
nym-task = { path = "../../common/task" }
|
nym-task = { path = "../../common/task" }
|
||||||
wasm-utils = { path = "../../common/wasm-utils", features = ["storage"], default-features = false }
|
|
||||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||||
@@ -67,7 +64,7 @@ wee_alloc = { version = "0.4", optional = true }
|
|||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.release]
|
[package.metadata.wasm-pack.profile.release]
|
||||||
wasm-opt = false
|
wasm-opt = true
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
@@ -28,10 +28,7 @@ const {
|
|||||||
set_panic_hook,
|
set_panic_hook,
|
||||||
Config,
|
Config,
|
||||||
GatewayEndpointConfig,
|
GatewayEndpointConfig,
|
||||||
ClientStorage,
|
|
||||||
current_network_topology,
|
current_network_topology,
|
||||||
make_key,
|
|
||||||
make_key2
|
|
||||||
} = wasm_bindgen;
|
} = wasm_bindgen;
|
||||||
|
|
||||||
let client = null;
|
let client = null;
|
||||||
@@ -106,25 +103,29 @@ function printAndDisplayTestResult(result) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dummyGatewayConfig() {
|
||||||
|
return new GatewayEndpointConfig(
|
||||||
|
'336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9',
|
||||||
|
'n1rqqw8km7a0rvf8lr6k8dsdqvvkyn2mglj7xxfm',
|
||||||
|
'ws://85.159.212.96:9000',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async function testWithTester() {
|
async function testWithTester() {
|
||||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
const gatewayConfig = dummyGatewayConfig();
|
||||||
|
|
||||||
// A) construct with hardcoded topology
|
// A) construct with hardcoded topology
|
||||||
const topology = dummyTopology()
|
const topology = dummyTopology()
|
||||||
const nodeTester = await new NymNodeTester(topology, preferredGateway);
|
const nodeTester = await new NymNodeTester(gatewayConfig, topology);
|
||||||
|
|
||||||
// B) first get topology directly from nym-api
|
// B) first get topology directly from nym-api
|
||||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||||
// const topology = await current_network_topology(validator)
|
// const topology = await current_network_topology(validator)
|
||||||
// const nodeTester = await new NymNodeTester(topology, preferredGateway);
|
// const nodeTester = await new NymNodeTester(gatewayConfig, topology);
|
||||||
//
|
//
|
||||||
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
|
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
|
||||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||||
// const nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway)
|
// const nodeTester = await NymNodeTester.new_with_api(gatewayConfig, validator)
|
||||||
|
|
||||||
// D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used
|
|
||||||
// const topology = dummyTopology()
|
|
||||||
// const nodeTester = await new NymNodeTester(topology);
|
|
||||||
|
|
||||||
self.onmessage = async event => {
|
self.onmessage = async event => {
|
||||||
if (event.data && event.data.kind) {
|
if (event.data && event.data.kind) {
|
||||||
@@ -142,7 +143,7 @@ async function testWithTester() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function testWithNymClient() {
|
async function testWithNymClient() {
|
||||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
const gatewayConfig = dummyGatewayConfig();
|
||||||
const topology = dummyTopology()
|
const topology = dummyTopology()
|
||||||
|
|
||||||
let received = 0
|
let received = 0
|
||||||
@@ -164,7 +165,7 @@ async function testWithNymClient() {
|
|||||||
|
|
||||||
console.log('Instantiating WASM client...');
|
console.log('Instantiating WASM client...');
|
||||||
|
|
||||||
let clientBuilder = NymClientBuilder.new_tester(topology, onMessageHandler, preferredGateway)
|
let clientBuilder = NymClientBuilder.new_tester(gatewayConfig, topology, onMessageHandler)
|
||||||
console.log('Web worker creating WASM client...');
|
console.log('Web worker creating WASM client...');
|
||||||
let local_client = await clientBuilder.start_client();
|
let local_client = await clientBuilder.start_client();
|
||||||
console.log('WASM client running!');
|
console.log('WASM client running!');
|
||||||
@@ -222,10 +223,10 @@ async function normalNymClientUsage() {
|
|||||||
|
|
||||||
debug.topology_refresh_rate_ms = BigInt(60000)
|
debug.topology_refresh_rate_ms = BigInt(60000)
|
||||||
|
|
||||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
const gatewayConfig = dummyGatewayConfig();
|
||||||
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||||
|
|
||||||
const config = new Config('my-awesome-wasm-client', validator, debug);
|
const config = new Config('my-awesome-wasm-client', validator, gatewayConfig, debug);
|
||||||
|
|
||||||
const onMessageHandler = (message) => {
|
const onMessageHandler = (message) => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
@@ -269,57 +270,6 @@ async function normalNymClientUsage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
async function messWithStorage() {
|
|
||||||
self.onmessage = async event => {
|
|
||||||
if (event.data && event.data.kind) {
|
|
||||||
switch (event.data.kind) {
|
|
||||||
case 'TestPacket': {
|
|
||||||
const { mixnodeIdentity } = event.data.args;
|
|
||||||
console.log("button clicked...", mixnodeIdentity);
|
|
||||||
|
|
||||||
let id1 = "one";
|
|
||||||
let id2 = "two";
|
|
||||||
|
|
||||||
console.log("making store1 NO-ENC");
|
|
||||||
let _storage1 = await ClientStorage.new_unencrypted(id1);
|
|
||||||
|
|
||||||
console.log("making store2 ENC")
|
|
||||||
let _storage2 = await new ClientStorage(id2, "my-secret-password");
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// console.log("attempting to use store1 WITH PASSWORD")
|
|
||||||
// let _storage1_alt = await new ClientStorage(id1, "password");
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// console.log("attempting to use store2 WITHOUT PASSWORD")
|
|
||||||
// let _storage2_alt = await ClientStorage.new_unencrypted(id2);
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// console.log("attempting to use store2 with WRONG PASSWORD")
|
|
||||||
// let _storage2_bad = await new ClientStorage(id2, "bad-password")
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// console.log("read1: ", await storage1.read());
|
|
||||||
// console.log("read2: ", await storage2.read());
|
|
||||||
//
|
|
||||||
// console.log("store1: ", await storage1.store("FOOMP"));
|
|
||||||
//
|
|
||||||
// console.log("read1: ", await storage1.read());
|
|
||||||
// console.log("read2: ", await storage2.read());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -331,13 +281,13 @@ async function main() {
|
|||||||
set_panic_hook();
|
set_panic_hook();
|
||||||
|
|
||||||
// run test on simplified and dedicated tester:
|
// run test on simplified and dedicated tester:
|
||||||
// await testWithTester()
|
await testWithTester()
|
||||||
|
|
||||||
// hook-up the whole client for testing
|
// hook-up the whole client for testing
|
||||||
// await testWithNymClient()
|
// await testWithNymClient()
|
||||||
|
|
||||||
// 'Normal' client setup (to send 'normal' messages)
|
// 'Normal' client setup (to send 'normal' messages)
|
||||||
await normalNymClientUsage()
|
// await normalNymClientUsage()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's get started!
|
// Let's get started!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
|
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
|
||||||
@@ -7,49 +7,60 @@
|
|||||||
#![allow(clippy::drop_copy)]
|
#![allow(clippy::drop_copy)]
|
||||||
|
|
||||||
use nym_client_core::config::{
|
use nym_client_core::config::{
|
||||||
Acknowledgements as ConfigAcknowledgements, Config as BaseClientConfig,
|
Acknowledgements as ConfigAcknowledgements, CoverTraffic as ConfigCoverTraffic,
|
||||||
CoverTraffic as ConfigCoverTraffic, DebugConfig as ConfigDebug,
|
DebugConfig as ConfigDebug, GatewayConnection as ConfigGatewayConnection,
|
||||||
GatewayConnection as ConfigGatewayConnection, ReplySurbs as ConfigReplySurbs,
|
GatewayEndpointConfig, ReplySurbs as ConfigReplySurbs, Topology as ConfigTopology,
|
||||||
Topology as ConfigTopology, Traffic as ConfigTraffic,
|
Traffic as ConfigTraffic,
|
||||||
};
|
};
|
||||||
use nym_sphinx::params::{PacketSize, PacketType};
|
use nym_sphinx::params::PacketSize;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use url::Url;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub(crate) base: BaseClientConfig,
|
/// ID specifies the human readable ID of this particular client.
|
||||||
|
pub(crate) id: String,
|
||||||
|
|
||||||
|
pub(crate) nym_api_url: Option<Url>,
|
||||||
|
|
||||||
|
pub(crate) disabled_credentials_mode: bool,
|
||||||
|
|
||||||
|
/// Information regarding how the client should send data to gateway.
|
||||||
|
pub(crate) gateway_endpoint: GatewayEndpointConfig,
|
||||||
|
|
||||||
|
pub(crate) debug: ConfigDebug,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl Config {
|
impl Config {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self {
|
pub fn new(
|
||||||
|
id: String,
|
||||||
|
validator_server: String,
|
||||||
|
gateway_endpoint: GatewayEndpointConfig,
|
||||||
|
debug: Option<Debug>,
|
||||||
|
) -> Self {
|
||||||
Config {
|
Config {
|
||||||
base: BaseClientConfig::new(id, env!("CARGO_PKG_VERSION").to_string())
|
id,
|
||||||
.with_custom_nyxd(vec![validator_server
|
nym_api_url: Some(
|
||||||
|
validator_server
|
||||||
.parse()
|
.parse()
|
||||||
.expect("provided url was malformed")])
|
.expect("provided url was malformed"),
|
||||||
.with_debug_config(debug.map(Into::into).unwrap_or_default()),
|
),
|
||||||
}
|
disabled_credentials_mode: true,
|
||||||
}
|
gateway_endpoint,
|
||||||
|
debug: debug.map(Into::into).unwrap_or_default(),
|
||||||
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self {
|
|
||||||
Config {
|
|
||||||
base: BaseClientConfig::new(id.into(), env!("CARGO_PKG_VERSION").to_string())
|
|
||||||
.with_disabled_credentials(true)
|
|
||||||
.with_disabled_cover_traffic(true)
|
|
||||||
.with_disabled_topology_refresh(true),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct TrafficWasm {
|
pub struct Traffic {
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// sent packet is going to be delayed at any given mix node.
|
/// 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
|
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
||||||
@@ -68,23 +79,14 @@ pub struct TrafficWasm {
|
|||||||
|
|
||||||
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
|
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
|
||||||
pub use_extended_packet_size: bool,
|
pub use_extended_packet_size: bool,
|
||||||
|
|
||||||
/// Controls whether the sent packets should use outfox as opposed to the default sphinx.
|
|
||||||
pub use_outfox: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TrafficWasm> for ConfigTraffic {
|
impl From<Traffic> for ConfigTraffic {
|
||||||
fn from(traffic: TrafficWasm) -> Self {
|
fn from(traffic: Traffic) -> Self {
|
||||||
let use_extended_packet_size = traffic
|
let use_extended_packet_size = traffic
|
||||||
.use_extended_packet_size
|
.use_extended_packet_size
|
||||||
.then(|| PacketSize::ExtendedPacket32);
|
.then(|| PacketSize::ExtendedPacket32);
|
||||||
|
|
||||||
let packet_type = if traffic.use_outfox {
|
|
||||||
PacketType::Outfox
|
|
||||||
} else {
|
|
||||||
PacketType::Mix
|
|
||||||
};
|
|
||||||
|
|
||||||
ConfigTraffic {
|
ConfigTraffic {
|
||||||
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
|
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
|
||||||
message_sending_average_delay: Duration::from_millis(
|
message_sending_average_delay: Duration::from_millis(
|
||||||
@@ -94,28 +96,26 @@ impl From<TrafficWasm> for ConfigTraffic {
|
|||||||
.disable_main_poisson_packet_distribution,
|
.disable_main_poisson_packet_distribution,
|
||||||
primary_packet_size: PacketSize::RegularPacket,
|
primary_packet_size: PacketSize::RegularPacket,
|
||||||
secondary_packet_size: use_extended_packet_size,
|
secondary_packet_size: use_extended_packet_size,
|
||||||
packet_type,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigTraffic> for TrafficWasm {
|
impl From<ConfigTraffic> for Traffic {
|
||||||
fn from(traffic: ConfigTraffic) -> Self {
|
fn from(traffic: ConfigTraffic) -> Self {
|
||||||
TrafficWasm {
|
Traffic {
|
||||||
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
|
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
|
||||||
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
|
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
|
||||||
as u64,
|
as u64,
|
||||||
disable_main_poisson_packet_distribution: traffic
|
disable_main_poisson_packet_distribution: traffic
|
||||||
.disable_main_poisson_packet_distribution,
|
.disable_main_poisson_packet_distribution,
|
||||||
use_extended_packet_size: traffic.secondary_packet_size.is_some(),
|
use_extended_packet_size: traffic.secondary_packet_size.is_some(),
|
||||||
use_outfox: traffic.packet_type == PacketType::Outfox,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct CoverTrafficWasm {
|
pub struct CoverTraffic {
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// it is going to take for another loop cover traffic message to be sent.
|
/// it is going to take for another loop cover traffic message to be sent.
|
||||||
pub loop_cover_traffic_average_delay_ms: u64,
|
pub loop_cover_traffic_average_delay_ms: u64,
|
||||||
@@ -129,8 +129,8 @@ pub struct CoverTrafficWasm {
|
|||||||
pub disable_loop_cover_traffic_stream: bool,
|
pub disable_loop_cover_traffic_stream: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CoverTrafficWasm> for ConfigCoverTraffic {
|
impl From<CoverTraffic> for ConfigCoverTraffic {
|
||||||
fn from(cover_traffic: CoverTrafficWasm) -> Self {
|
fn from(cover_traffic: CoverTraffic) -> Self {
|
||||||
ConfigCoverTraffic {
|
ConfigCoverTraffic {
|
||||||
loop_cover_traffic_average_delay: Duration::from_millis(
|
loop_cover_traffic_average_delay: Duration::from_millis(
|
||||||
cover_traffic.loop_cover_traffic_average_delay_ms,
|
cover_traffic.loop_cover_traffic_average_delay_ms,
|
||||||
@@ -141,9 +141,9 @@ impl From<CoverTrafficWasm> for ConfigCoverTraffic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigCoverTraffic> for CoverTrafficWasm {
|
impl From<ConfigCoverTraffic> for CoverTraffic {
|
||||||
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
|
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
|
||||||
CoverTrafficWasm {
|
CoverTraffic {
|
||||||
loop_cover_traffic_average_delay_ms: cover_traffic
|
loop_cover_traffic_average_delay_ms: cover_traffic
|
||||||
.loop_cover_traffic_average_delay
|
.loop_cover_traffic_average_delay
|
||||||
.as_millis() as u64,
|
.as_millis() as u64,
|
||||||
@@ -155,14 +155,14 @@ impl From<ConfigCoverTraffic> for CoverTrafficWasm {
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct GatewayConnectionWasm {
|
pub struct GatewayConnection {
|
||||||
/// How long we're willing to wait for a response to a message sent to the gateway,
|
/// How long we're willing to wait for a response to a message sent to the gateway,
|
||||||
/// before giving up on it.
|
/// before giving up on it.
|
||||||
pub gateway_response_timeout_ms: u64,
|
pub gateway_response_timeout_ms: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<GatewayConnectionWasm> for ConfigGatewayConnection {
|
impl From<GatewayConnection> for ConfigGatewayConnection {
|
||||||
fn from(gateway_connection: GatewayConnectionWasm) -> Self {
|
fn from(gateway_connection: GatewayConnection) -> Self {
|
||||||
ConfigGatewayConnection {
|
ConfigGatewayConnection {
|
||||||
gateway_response_timeout: Duration::from_millis(
|
gateway_response_timeout: Duration::from_millis(
|
||||||
gateway_connection.gateway_response_timeout_ms,
|
gateway_connection.gateway_response_timeout_ms,
|
||||||
@@ -171,9 +171,9 @@ impl From<GatewayConnectionWasm> for ConfigGatewayConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigGatewayConnection> for GatewayConnectionWasm {
|
impl From<ConfigGatewayConnection> for GatewayConnection {
|
||||||
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
|
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
|
||||||
GatewayConnectionWasm {
|
GatewayConnection {
|
||||||
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
|
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
|
||||||
as u64,
|
as u64,
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ impl From<ConfigGatewayConnection> for GatewayConnectionWasm {
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct AcknowledgementsWasm {
|
pub struct Acknowledgements {
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// sent acknowledgement is going to be delayed at any given mix node.
|
/// 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
|
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
||||||
@@ -200,8 +200,8 @@ pub struct AcknowledgementsWasm {
|
|||||||
pub ack_wait_addition_ms: u64,
|
pub ack_wait_addition_ms: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AcknowledgementsWasm> for ConfigAcknowledgements {
|
impl From<Acknowledgements> for ConfigAcknowledgements {
|
||||||
fn from(acknowledgements: AcknowledgementsWasm) -> Self {
|
fn from(acknowledgements: Acknowledgements) -> Self {
|
||||||
ConfigAcknowledgements {
|
ConfigAcknowledgements {
|
||||||
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
|
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
|
||||||
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||||
@@ -210,9 +210,9 @@ impl From<AcknowledgementsWasm> for ConfigAcknowledgements {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigAcknowledgements> for AcknowledgementsWasm {
|
impl From<ConfigAcknowledgements> for Acknowledgements {
|
||||||
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
|
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
|
||||||
AcknowledgementsWasm {
|
Acknowledgements {
|
||||||
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
|
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
|
||||||
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||||
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
|
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
|
||||||
@@ -222,7 +222,7 @@ impl From<ConfigAcknowledgements> for AcknowledgementsWasm {
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct TopologyWasm {
|
pub struct Topology {
|
||||||
/// The uniform delay every which clients are querying the directory server
|
/// The uniform delay every which clients are querying the directory server
|
||||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||||
pub topology_refresh_rate_ms: u64,
|
pub topology_refresh_rate_ms: u64,
|
||||||
@@ -238,8 +238,8 @@ pub struct TopologyWasm {
|
|||||||
pub disable_refreshing: bool,
|
pub disable_refreshing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TopologyWasm> for ConfigTopology {
|
impl From<Topology> for ConfigTopology {
|
||||||
fn from(topology: TopologyWasm) -> Self {
|
fn from(topology: Topology) -> Self {
|
||||||
ConfigTopology {
|
ConfigTopology {
|
||||||
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
|
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
|
||||||
topology_resolution_timeout: Duration::from_millis(
|
topology_resolution_timeout: Duration::from_millis(
|
||||||
@@ -250,9 +250,9 @@ impl From<TopologyWasm> for ConfigTopology {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigTopology> for TopologyWasm {
|
impl From<ConfigTopology> for Topology {
|
||||||
fn from(topology: ConfigTopology) -> Self {
|
fn from(topology: ConfigTopology) -> Self {
|
||||||
TopologyWasm {
|
Topology {
|
||||||
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
|
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
|
||||||
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
|
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
|
||||||
disable_refreshing: topology.disable_refreshing,
|
disable_refreshing: topology.disable_refreshing,
|
||||||
@@ -262,7 +262,7 @@ impl From<ConfigTopology> for TopologyWasm {
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ReplySurbsWasm {
|
pub struct ReplySurbs {
|
||||||
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
/// 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.
|
/// It can only allow to go below that value if its to request additional reply surbs.
|
||||||
pub minimum_reply_surb_storage_threshold: usize,
|
pub minimum_reply_surb_storage_threshold: usize,
|
||||||
@@ -296,8 +296,8 @@ pub struct ReplySurbsWasm {
|
|||||||
pub maximum_reply_key_age_ms: u64,
|
pub maximum_reply_key_age_ms: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ReplySurbsWasm> for ConfigReplySurbs {
|
impl From<ReplySurbs> for ConfigReplySurbs {
|
||||||
fn from(reply_surbs: ReplySurbsWasm) -> Self {
|
fn from(reply_surbs: ReplySurbs) -> Self {
|
||||||
ConfigReplySurbs {
|
ConfigReplySurbs {
|
||||||
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
|
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
|
||||||
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
|
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
|
||||||
@@ -317,9 +317,9 @@ impl From<ReplySurbsWasm> for ConfigReplySurbs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigReplySurbs> for ReplySurbsWasm {
|
impl From<ConfigReplySurbs> for ReplySurbs {
|
||||||
fn from(reply_surbs: ConfigReplySurbs) -> Self {
|
fn from(reply_surbs: ConfigReplySurbs) -> Self {
|
||||||
ReplySurbsWasm {
|
ReplySurbs {
|
||||||
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
|
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
|
||||||
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
|
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
|
||||||
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
|
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
|
||||||
@@ -341,28 +341,28 @@ impl From<ConfigReplySurbs> for ReplySurbsWasm {
|
|||||||
// just a helper structure to more easily pass through the JS boundary
|
// just a helper structure to more easily pass through the JS boundary
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct DebugWasm {
|
pub struct Debug {
|
||||||
/// Defines all configuration options related to traffic streams.
|
/// Defines all configuration options related to traffic streams.
|
||||||
pub traffic: TrafficWasm,
|
pub traffic: Traffic,
|
||||||
|
|
||||||
/// Defines all configuration options related to cover traffic stream(s).
|
/// Defines all configuration options related to cover traffic stream(s).
|
||||||
pub cover_traffic: CoverTrafficWasm,
|
pub cover_traffic: CoverTraffic,
|
||||||
|
|
||||||
/// Defines all configuration options related to the gateway connection.
|
/// Defines all configuration options related to the gateway connection.
|
||||||
pub gateway_connection: GatewayConnectionWasm,
|
pub gateway_connection: GatewayConnection,
|
||||||
|
|
||||||
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
|
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
|
||||||
pub acknowledgements: AcknowledgementsWasm,
|
pub acknowledgements: Acknowledgements,
|
||||||
|
|
||||||
/// Defines all configuration options related topology, such as refresh rates or timeouts.
|
/// Defines all configuration options related topology, such as refresh rates or timeouts.
|
||||||
pub topology: TopologyWasm,
|
pub topology: Topology,
|
||||||
|
|
||||||
/// Defines all configuration options related to reply SURBs.
|
/// Defines all configuration options related to reply SURBs.
|
||||||
pub reply_surbs: ReplySurbsWasm,
|
pub reply_surbs: ReplySurbs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DebugWasm> for ConfigDebug {
|
impl From<Debug> for ConfigDebug {
|
||||||
fn from(debug: DebugWasm) -> Self {
|
fn from(debug: Debug) -> Self {
|
||||||
ConfigDebug {
|
ConfigDebug {
|
||||||
traffic: debug.traffic.into(),
|
traffic: debug.traffic.into(),
|
||||||
cover_traffic: debug.cover_traffic.into(),
|
cover_traffic: debug.cover_traffic.into(),
|
||||||
@@ -374,9 +374,9 @@ impl From<DebugWasm> for ConfigDebug {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigDebug> for DebugWasm {
|
impl From<ConfigDebug> for Debug {
|
||||||
fn from(debug: ConfigDebug) -> Self {
|
fn from(debug: ConfigDebug) -> Self {
|
||||||
DebugWasm {
|
Debug {
|
||||||
traffic: debug.traffic.into(),
|
traffic: debug.traffic.into(),
|
||||||
cover_traffic: debug.cover_traffic.into(),
|
cover_traffic: debug.cover_traffic.into(),
|
||||||
gateway_connection: debug.gateway_connection.into(),
|
gateway_connection: debug.gateway_connection.into(),
|
||||||
@@ -388,6 +388,6 @@ impl From<ConfigDebug> for DebugWasm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn default_debug() -> DebugWasm {
|
pub fn default_debug() -> Debug {
|
||||||
ConfigDebug::default().into()
|
ConfigDebug::default().into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use std::sync::Arc;
|
|||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen::JsValue;
|
use wasm_bindgen::JsValue;
|
||||||
use wasm_bindgen_futures::future_to_promise;
|
use wasm_bindgen_futures::future_to_promise;
|
||||||
use wasm_utils::{console_log, simple_js_error};
|
use wasm_utils::{console_log, js_error, simple_js_error};
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct NymClientTestRequest {
|
pub struct NymClientTestRequest {
|
||||||
@@ -142,9 +142,20 @@ impl WasmTopologyExt for Arc<ClientState> {
|
|||||||
return Err(WasmClientError::NonExistentMixnode { mixnode_identity }.into());
|
return Err(WasmClientError::NonExistentMixnode { mixnode_identity }.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let ext = WasmTestMessageExt::new(test_id);
|
let mut test_msgs = Vec::with_capacity(num_test_packets as usize);
|
||||||
let test_msgs = NodeTestMessage::mix_plaintexts(mix, num_test_packets, ext)
|
for i in 1..=num_test_packets {
|
||||||
.map_err(WasmClientError::from)?;
|
let msg = NodeTestMessage::new_mix(
|
||||||
|
mix,
|
||||||
|
i,
|
||||||
|
num_test_packets,
|
||||||
|
WasmTestMessageExt::new(test_id),
|
||||||
|
);
|
||||||
|
let serialized = match msg.as_bytes() {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(err) => return Err(js_error!("failed to serialize test message: {err}")),
|
||||||
|
};
|
||||||
|
test_msgs.push(serialized);
|
||||||
|
}
|
||||||
|
|
||||||
let mut updated = current_topology.clone();
|
let mut updated = current_topology.clone();
|
||||||
updated.set_mixes_in_layer(mix.layer.into(), vec![mix.to_owned()]);
|
updated.set_mixes_in_layer(mix.layer.into(), vec![mix.to_owned()]);
|
||||||
|
|||||||
@@ -4,28 +4,28 @@
|
|||||||
use self::config::Config;
|
use self::config::Config;
|
||||||
use crate::client::helpers::{InputSender, NymClientTestRequest, WasmTopologyExt};
|
use crate::client::helpers::{InputSender, NymClientTestRequest, WasmTopologyExt};
|
||||||
use crate::client::response_pusher::ResponsePusher;
|
use crate::client::response_pusher::ResponsePusher;
|
||||||
use crate::constants::NODE_TESTER_CLIENT_ID;
|
|
||||||
use crate::error::WasmClientError;
|
use crate::error::WasmClientError;
|
||||||
use crate::helpers::{
|
use crate::helpers::{
|
||||||
parse_recipient, parse_sender_tag, setup_from_topology, setup_gateway_from_api,
|
parse_recipient, parse_sender_tag, setup_new_key_manager, setup_reply_surb_storage_backend,
|
||||||
setup_reply_surb_storage_backend,
|
|
||||||
};
|
};
|
||||||
use crate::storage::traits::FullWasmClientStorage;
|
|
||||||
use crate::storage::ClientStorage;
|
|
||||||
use crate::topology::WasmNymTopology;
|
use crate::topology::WasmNymTopology;
|
||||||
use js_sys::Promise;
|
use js_sys::Promise;
|
||||||
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
|
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
|
||||||
|
use nym_bandwidth_controller::BandwidthController;
|
||||||
use nym_client_core::client::base_client::{
|
use nym_client_core::client::base_client::{
|
||||||
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
BaseClientBuilder, ClientInput, ClientOutput, ClientState, CredentialsToggle,
|
||||||
};
|
};
|
||||||
use nym_client_core::client::inbound_messages::InputMessage;
|
use nym_client_core::client::replies::reply_storage::browser_backend;
|
||||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
use nym_client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager};
|
||||||
|
use nym_client_core::config::{
|
||||||
|
CoverTraffic, DebugConfig, GatewayEndpointConfig, Topology, Traffic,
|
||||||
|
};
|
||||||
|
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
|
||||||
use nym_sphinx::params::PacketType;
|
use nym_sphinx::params::PacketType;
|
||||||
use nym_task::connections::TransmissionLane;
|
use nym_task::connections::TransmissionLane;
|
||||||
use nym_task::TaskManager;
|
use nym_task::TaskManager;
|
||||||
use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
|
use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
|
||||||
use nym_topology::NymTopology;
|
use nym_topology::NymTopology;
|
||||||
use nym_validator_client::client::IdentityKey;
|
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -50,35 +50,42 @@ pub struct NymClient {
|
|||||||
// even though we don't use graceful shutdowns, other components rely on existence of this struct
|
// even though we don't use graceful shutdowns, other components rely on existence of this struct
|
||||||
// and if it's dropped, everything will start going offline
|
// and if it's dropped, everything will start going offline
|
||||||
_task_manager: TaskManager,
|
_task_manager: TaskManager,
|
||||||
|
packet_type: Option<PacketType>,
|
||||||
packet_type: PacketType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct NymClientBuilder {
|
pub struct NymClientBuilder {
|
||||||
config: Config,
|
config: Config,
|
||||||
custom_topology: Option<NymTopology>,
|
custom_topology: Option<NymTopology>,
|
||||||
preferred_gateway: Option<IdentityKey>,
|
|
||||||
|
|
||||||
storage_passphrase: Option<String>,
|
/// KeyManager object containing smart pointers to all relevant keys used by the client.
|
||||||
|
key_manager: KeyManager,
|
||||||
|
|
||||||
|
reply_surb_storage_backend: browser_backend::Backend,
|
||||||
|
|
||||||
on_message: js_sys::Function,
|
on_message: js_sys::Function,
|
||||||
|
|
||||||
|
// unimplemented:
|
||||||
|
bandwidth_controller:
|
||||||
|
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
|
||||||
|
disabled_credentials: bool,
|
||||||
|
packet_type: Option<PacketType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl NymClientBuilder {
|
impl NymClientBuilder {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(
|
pub fn new(config: Config, on_message: js_sys::Function) -> Self {
|
||||||
config: Config,
|
//, key_manager: Option<KeyManager>) {
|
||||||
on_message: js_sys::Function,
|
|
||||||
preferred_gateway: Option<IdentityKey>,
|
|
||||||
storage_passphrase: Option<String>,
|
|
||||||
) -> Self {
|
|
||||||
NymClientBuilder {
|
NymClientBuilder {
|
||||||
|
reply_surb_storage_backend: setup_reply_surb_storage_backend(config.debug.reply_surbs),
|
||||||
config,
|
config,
|
||||||
custom_topology: None,
|
custom_topology: None,
|
||||||
storage_passphrase,
|
key_manager: setup_new_key_manager(),
|
||||||
on_message,
|
on_message,
|
||||||
preferred_gateway,
|
bandwidth_controller: None,
|
||||||
|
disabled_credentials: true,
|
||||||
|
packet_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,24 +94,49 @@ impl NymClientBuilder {
|
|||||||
// hardcoded topology
|
// hardcoded topology
|
||||||
// NOTE: you most likely want to use `[NymNodeTester]` instead.
|
// NOTE: you most likely want to use `[NymNodeTester]` instead.
|
||||||
pub fn new_tester(
|
pub fn new_tester(
|
||||||
|
gateway_config: GatewayEndpointConfig,
|
||||||
topology: WasmNymTopology,
|
topology: WasmNymTopology,
|
||||||
on_message: js_sys::Function,
|
on_message: js_sys::Function,
|
||||||
gateway: Option<IdentityKey>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if let Some(gateway_id) = &gateway {
|
if !topology.ensure_contains(&gateway_config) {
|
||||||
if !topology.ensure_contains_gateway_id(gateway_id) {
|
panic!("the specified topology does not contain the gateway used by the client")
|
||||||
panic!("the specified topology does not contain the gateway used by the client")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID);
|
let full_config = Config {
|
||||||
|
id: "ephemeral-id".to_string(),
|
||||||
|
nym_api_url: None,
|
||||||
|
disabled_credentials_mode: true,
|
||||||
|
gateway_endpoint: gateway_config,
|
||||||
|
debug: DebugConfig {
|
||||||
|
traffic: Traffic {
|
||||||
|
disable_main_poisson_packet_distribution: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
cover_traffic: CoverTraffic {
|
||||||
|
disable_loop_cover_traffic_stream: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
topology: Topology {
|
||||||
|
disable_refreshing: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
NymClientBuilder {
|
NymClientBuilder {
|
||||||
|
reply_surb_storage_backend: setup_reply_surb_storage_backend(
|
||||||
|
full_config.debug.reply_surbs,
|
||||||
|
),
|
||||||
config: full_config,
|
config: full_config,
|
||||||
custom_topology: Some(topology.into()),
|
custom_topology: Some(topology.into()),
|
||||||
|
// TODO: once we make keys persistent, we'll require some kind of `init` method to generate
|
||||||
|
// a prior shared keypair between the client and the gateway
|
||||||
|
key_manager: setup_new_key_manager(),
|
||||||
on_message,
|
on_message,
|
||||||
storage_passphrase: None,
|
bandwidth_controller: None,
|
||||||
preferred_gateway: gateway,
|
disabled_credentials: true,
|
||||||
|
packet_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +144,7 @@ impl NymClientBuilder {
|
|||||||
ResponsePusher::new(client_output, on_message).start()
|
ResponsePusher::new(client_output, on_message).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn topology_provider(&mut self) -> Option<Box<dyn TopologyProvider + Send + Sync>> {
|
fn topology_provider(&mut self) -> Option<Box<dyn TopologyProvider>> {
|
||||||
if let Some(hardcoded_topology) = self.custom_topology.take() {
|
if let Some(hardcoded_topology) = self.custom_topology.take() {
|
||||||
Some(Box::new(HardcodedTopologyProvider::new(hardcoded_topology)))
|
Some(Box::new(HardcodedTopologyProvider::new(hardcoded_topology)))
|
||||||
} else {
|
} else {
|
||||||
@@ -120,49 +152,36 @@ impl NymClientBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialise_storage(config: &Config, base_storage: ClientStorage) -> FullWasmClientStorage {
|
|
||||||
FullWasmClientStorage {
|
|
||||||
keys_and_gateway_store: base_storage,
|
|
||||||
reply_storage: setup_reply_surb_storage_backend(config.base.debug.reply_surbs),
|
|
||||||
credential_storage: EphemeralCredentialStorage::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> {
|
async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> {
|
||||||
console_log!("Starting the wasm client");
|
console_log!("Starting the wasm client");
|
||||||
|
|
||||||
let nym_api_endpoints = self.config.base.client.nym_api_urls.clone();
|
|
||||||
|
|
||||||
// TODO: this will have to be re-used for surbs. but this is a problem for another PR.
|
|
||||||
let client_store =
|
|
||||||
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let user_chosen = self.preferred_gateway.clone();
|
|
||||||
|
|
||||||
// if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way
|
|
||||||
if let Some(topology) = &self.custom_topology {
|
|
||||||
setup_from_topology(user_chosen, topology, &client_store).await?
|
|
||||||
} else {
|
|
||||||
setup_gateway_from_api(&client_store, user_chosen, &nym_api_endpoints).await?
|
|
||||||
};
|
|
||||||
|
|
||||||
let packet_type = self.config.base.debug.traffic.packet_type;
|
|
||||||
let storage = Self::initialise_storage(&self.config, client_store);
|
|
||||||
let maybe_topology_provider = self.topology_provider();
|
let maybe_topology_provider = self.topology_provider();
|
||||||
|
|
||||||
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> =
|
let disabled_credentials = if self.disabled_credentials {
|
||||||
BaseClientBuilder::<FakeClient<DirectSigningNyxdClient>, _>::new(
|
CredentialsToggle::Disabled
|
||||||
&self.config.base,
|
} else {
|
||||||
storage,
|
CredentialsToggle::Enabled
|
||||||
None,
|
};
|
||||||
);
|
|
||||||
|
let nym_api_endpoints = match self.config.nym_api_url {
|
||||||
|
Some(endpoint) => vec![endpoint],
|
||||||
|
None => Vec::new(),
|
||||||
|
};
|
||||||
|
let mut base_builder = BaseClientBuilder::new(
|
||||||
|
&self.config.gateway_endpoint,
|
||||||
|
&self.config.debug,
|
||||||
|
self.key_manager,
|
||||||
|
self.bandwidth_controller,
|
||||||
|
self.reply_surb_storage_backend,
|
||||||
|
disabled_credentials,
|
||||||
|
nym_api_endpoints,
|
||||||
|
);
|
||||||
if let Some(topology_provider) = maybe_topology_provider {
|
if let Some(topology_provider) = maybe_topology_provider {
|
||||||
base_builder = base_builder.with_topology_provider(topology_provider);
|
base_builder = base_builder.with_topology_provider(topology_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let self_address = base_builder.as_mix_recipient().to_string();
|
||||||
let mut started_client = base_builder.start_base().await?;
|
let mut started_client = base_builder.start_base().await?;
|
||||||
let self_address = started_client.address.to_string();
|
|
||||||
|
|
||||||
let client_input = started_client.client_input.register_producer();
|
let client_input = started_client.client_input.register_producer();
|
||||||
let client_output = started_client.client_output.register_consumer();
|
let client_output = started_client.client_output.register_consumer();
|
||||||
@@ -175,7 +194,7 @@ impl NymClientBuilder {
|
|||||||
client_state: Arc::new(started_client.client_state),
|
client_state: Arc::new(started_client.client_state),
|
||||||
_full_topology: None,
|
_full_topology: None,
|
||||||
_task_manager: started_client.task_manager,
|
_task_manager: started_client.task_manager,
|
||||||
packet_type,
|
packet_type: self.packet_type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,27 +208,16 @@ impl NymClient {
|
|||||||
async fn _new(
|
async fn _new(
|
||||||
config: Config,
|
config: Config,
|
||||||
on_message: js_sys::Function,
|
on_message: js_sys::Function,
|
||||||
preferred_gateway: Option<IdentityKey>,
|
|
||||||
storage_passphrase: Option<String>,
|
|
||||||
) -> Result<NymClient, WasmClientError> {
|
) -> Result<NymClient, WasmClientError> {
|
||||||
NymClientBuilder::new(config, on_message, preferred_gateway, storage_passphrase)
|
NymClientBuilder::new(config, on_message)
|
||||||
.start_client_async()
|
.start_client_async()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(
|
pub fn new(config: Config, on_message: js_sys::Function) -> Promise {
|
||||||
config: Config,
|
future_to_promise(async move { Self::_new(config, on_message).await.into_promise_result() })
|
||||||
on_message: js_sys::Function,
|
|
||||||
preferred_gateway: Option<IdentityKey>,
|
|
||||||
storage_passphrase: Option<String>,
|
|
||||||
) -> Promise {
|
|
||||||
future_to_promise(async move {
|
|
||||||
Self::_new(config, on_message, preferred_gateway, storage_passphrase)
|
|
||||||
.await
|
|
||||||
.into_promise_result()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_address(&self) -> String {
|
pub fn self_address(&self) -> String {
|
||||||
@@ -273,7 +281,7 @@ impl NymClient {
|
|||||||
|
|
||||||
let lane = TransmissionLane::General;
|
let lane = TransmissionLane::General;
|
||||||
|
|
||||||
let input_msg = InputMessage::new_regular(recipient, message, lane, Some(self.packet_type));
|
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
|
||||||
self.client_input.send_message(input_msg)
|
self.client_input.send_message(input_msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,13 +308,8 @@ impl NymClient {
|
|||||||
|
|
||||||
let lane = TransmissionLane::General;
|
let lane = TransmissionLane::General;
|
||||||
|
|
||||||
let input_msg = InputMessage::new_anonymous(
|
let input_msg =
|
||||||
recipient,
|
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
|
||||||
message,
|
|
||||||
reply_surbs,
|
|
||||||
lane,
|
|
||||||
Some(self.packet_type),
|
|
||||||
);
|
|
||||||
self.client_input.send_message(input_msg)
|
self.client_input.send_message(input_msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +327,7 @@ impl NymClient {
|
|||||||
|
|
||||||
let lane = TransmissionLane::General;
|
let lane = TransmissionLane::General;
|
||||||
|
|
||||||
let input_msg = InputMessage::new_reply(sender_tag, message, lane, Some(self.packet_type));
|
let input_msg = InputMessage::new_reply(sender_tag, message, lane, self.packet_type);
|
||||||
self.client_input.send_message(input_msg)
|
self.client_input.send_message(input_msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
pub(crate) const NODE_TESTER_ID: &str = "_nym-node-tester";
|
|
||||||
pub(crate) const NODE_TESTER_CLIENT_ID: &str = "_nym-node-tester-client";
|
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::storage::errors::ClientStorageError;
|
|
||||||
use crate::topology::WasmTopologyError;
|
use crate::topology::WasmTopologyError;
|
||||||
use js_sys::Promise;
|
use js_sys::Promise;
|
||||||
use nym_client_core::config::GatewayEndpointConfig;
|
|
||||||
use nym_client_core::error::ClientCoreError;
|
use nym_client_core::error::ClientCoreError;
|
||||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||||
use nym_gateway_client::error::GatewayClientError;
|
use nym_gateway_client::error::GatewayClientError;
|
||||||
use nym_node_tester_utils::error::NetworkTestingError;
|
use nym_node_tester_utils::error::NetworkTestingError;
|
||||||
use nym_sphinx::addressing::clients::RecipientFormattingError;
|
use nym_sphinx::addressing::clients::RecipientFormattingError;
|
||||||
use nym_sphinx::anonymous_replies::requests::InvalidAnonymousSenderTagRepresentation;
|
use nym_sphinx::anonymous_replies::requests::InvalidAnonymousSenderTagRepresentation;
|
||||||
use nym_topology::NymTopologyError;
|
|
||||||
use nym_validator_client::ValidatorClientError;
|
use nym_validator_client::ValidatorClientError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wasm_bindgen::JsValue;
|
use wasm_bindgen::JsValue;
|
||||||
@@ -46,18 +43,12 @@ pub enum WasmClientError {
|
|||||||
source: ValidatorClientError,
|
source: ValidatorClientError,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("The provided wasm topology was invalid: {source}")]
|
#[error("The provided topology was invalid: {source}")]
|
||||||
WasmTopologyError {
|
WasmTopologyError {
|
||||||
#[from]
|
#[from]
|
||||||
source: WasmTopologyError,
|
source: WasmTopologyError,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("The provided nym topology was invalid: {source}")]
|
|
||||||
TopologyError {
|
|
||||||
#[from]
|
|
||||||
source: NymTopologyError,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("failed to test the node: {source}")]
|
#[error("failed to test the node: {source}")]
|
||||||
NodeTestingFailure {
|
NodeTestingFailure {
|
||||||
#[from]
|
#[from]
|
||||||
@@ -76,9 +67,6 @@ pub enum WasmClientError {
|
|||||||
#[error("Mixnode {mixnode_identity} is not present in the current network topology")]
|
#[error("Mixnode {mixnode_identity} is not present in the current network topology")]
|
||||||
NonExistentMixnode { mixnode_identity: String },
|
NonExistentMixnode { mixnode_identity: String },
|
||||||
|
|
||||||
#[error("Gateway {gateway_identity} is not present in the current network topology")]
|
|
||||||
NonExistentGateway { gateway_identity: String },
|
|
||||||
|
|
||||||
#[error("{raw} is not a valid Nym network recipient: {source}")]
|
#[error("{raw} is not a valid Nym network recipient: {source}")]
|
||||||
MalformedRecipient {
|
MalformedRecipient {
|
||||||
raw: String,
|
raw: String,
|
||||||
@@ -90,17 +78,6 @@ pub enum WasmClientError {
|
|||||||
raw: String,
|
raw: String,
|
||||||
source: InvalidAnonymousSenderTagRepresentation,
|
source: InvalidAnonymousSenderTagRepresentation,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error(transparent)]
|
|
||||||
StorageError {
|
|
||||||
#[from]
|
|
||||||
source: ClientStorageError,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("this client has already registered with a gateway: {gateway_config:?}")]
|
|
||||||
AlreadyRegistered {
|
|
||||||
gateway_config: GatewayEndpointConfig,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmClientError {
|
impl WasmClientError {
|
||||||
|
|||||||
@@ -2,26 +2,29 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::error::WasmClientError;
|
use crate::error::WasmClientError;
|
||||||
use crate::storage::ClientStorage;
|
|
||||||
use crate::topology::WasmNymTopology;
|
use crate::topology::WasmNymTopology;
|
||||||
use js_sys::Promise;
|
use js_sys::Promise;
|
||||||
|
use nym_client_core::client::key_manager::KeyManager;
|
||||||
use nym_client_core::client::replies::reply_storage::browser_backend;
|
use nym_client_core::client::replies::reply_storage::browser_backend;
|
||||||
use nym_client_core::config;
|
use nym_client_core::config;
|
||||||
use nym_client_core::init::helpers::current_gateways;
|
|
||||||
use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationDetails};
|
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||||
use nym_topology::{gateway, NymTopology};
|
use nym_topology::NymTopology;
|
||||||
use nym_validator_client::client::IdentityKey;
|
|
||||||
use nym_validator_client::NymApiClient;
|
use nym_validator_client::NymApiClient;
|
||||||
use rand::thread_rng;
|
use rand::rngs::OsRng;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
use wasm_bindgen_futures::future_to_promise;
|
use wasm_bindgen_futures::future_to_promise;
|
||||||
use wasm_utils::PromisableResult;
|
use wasm_utils::{console_log, PromisableResult};
|
||||||
|
|
||||||
|
pub(crate) fn setup_new_key_manager() -> KeyManager {
|
||||||
|
let mut rng = OsRng;
|
||||||
|
console_log!("generated new set of keys");
|
||||||
|
KeyManager::new(&mut rng)
|
||||||
|
}
|
||||||
|
|
||||||
// don't get too excited about the name, under the hood it's just a big fat placeholder
|
// don't get too excited about the name, under the hood it's just a big fat placeholder
|
||||||
// with no disk_persistence
|
// with no persistence
|
||||||
pub(crate) fn setup_reply_surb_storage_backend(
|
pub(crate) fn setup_reply_surb_storage_backend(
|
||||||
config: config::ReplySurbs,
|
config: config::ReplySurbs,
|
||||||
) -> browser_backend::Backend {
|
) -> browser_backend::Backend {
|
||||||
@@ -77,38 +80,3 @@ pub fn current_network_topology(nym_api_url: String) -> Promise {
|
|||||||
.into_promise_result()
|
.into_promise_result()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_gateway(
|
|
||||||
client_store: &ClientStorage,
|
|
||||||
chosen_gateway: Option<IdentityKey>,
|
|
||||||
gateways: &[gateway::Node],
|
|
||||||
) -> Result<InitialisationDetails, WasmClientError> {
|
|
||||||
let setup = if client_store.has_full_gateway_info().await? {
|
|
||||||
GatewaySetup::MustLoad
|
|
||||||
} else {
|
|
||||||
GatewaySetup::new_fresh(chosen_gateway.clone(), None)
|
|
||||||
};
|
|
||||||
|
|
||||||
setup_gateway_from(&setup, client_store, client_store, false, Some(gateways))
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn setup_gateway_from_api(
|
|
||||||
client_store: &ClientStorage,
|
|
||||||
chosen_gateway: Option<IdentityKey>,
|
|
||||||
nym_apis: &[Url],
|
|
||||||
) -> Result<InitialisationDetails, WasmClientError> {
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
let gateways = current_gateways(&mut rng, nym_apis).await?;
|
|
||||||
setup_gateway(client_store, chosen_gateway, &gateways).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn setup_from_topology(
|
|
||||||
explicit_gateway: Option<IdentityKey>,
|
|
||||||
topology: &NymTopology,
|
|
||||||
client_store: &ClientStorage,
|
|
||||||
) -> Result<InitialisationDetails, WasmClientError> {
|
|
||||||
let gateways = topology.gateways();
|
|
||||||
setup_gateway(client_store, explicit_gateway, gateways).await
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
@@ -7,13 +7,10 @@ use wasm_bindgen::prelude::*;
|
|||||||
mod client;
|
mod client;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub mod encoded_payload_helper;
|
pub mod encoded_payload_helper;
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub mod gateway_selector;
|
pub mod gateway_selector;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub mod storage;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
pub mod tester;
|
pub mod tester;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub mod topology;
|
pub mod topology;
|
||||||
@@ -23,8 +20,6 @@ pub mod validation;
|
|||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
mod constants;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn set_panic_hook() {
|
pub fn set_panic_hook() {
|
||||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use thiserror::Error;
|
|
||||||
use wasm_bindgen::JsValue;
|
|
||||||
use wasm_utils::simple_js_error;
|
|
||||||
use wasm_utils::storage::error::StorageError;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum ClientStorageError {
|
|
||||||
#[error("failed to use the storage: {source}")]
|
|
||||||
StorageError {
|
|
||||||
#[from]
|
|
||||||
source: StorageError,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("{typ} cryptographic key is not available in storage")]
|
|
||||||
CryptoKeyNotInStorage { typ: String },
|
|
||||||
|
|
||||||
#[error("the prior gateway details are not available in the storage")]
|
|
||||||
GatewayDetailsNotInStorage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ClientStorageError> for JsValue {
|
|
||||||
fn from(value: ClientStorageError) -> Self {
|
|
||||||
simple_js_error(value.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::client::config::Config;
|
|
||||||
use crate::storage::errors::ClientStorageError;
|
|
||||||
use js_sys::Promise;
|
|
||||||
use nym_client_core::client::base_client::storage::gateway_details::PersistedGatewayDetails;
|
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
|
||||||
use nym_gateway_client::SharedKeys;
|
|
||||||
use nym_sphinx::acknowledgements::AckKey;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
use wasm_bindgen_futures::future_to_promise;
|
|
||||||
use wasm_utils::storage::{IdbVersionChangeEvent, WasmStorage};
|
|
||||||
use wasm_utils::PromisableResult;
|
|
||||||
use zeroize::Zeroizing;
|
|
||||||
|
|
||||||
pub(crate) mod errors;
|
|
||||||
pub(crate) mod traits;
|
|
||||||
|
|
||||||
const STORAGE_NAME_PREFIX: &str = "wasm-client-storage";
|
|
||||||
const STORAGE_VERSION: u32 = 1;
|
|
||||||
|
|
||||||
// v1 tables
|
|
||||||
mod v1 {
|
|
||||||
// stores
|
|
||||||
pub const KEYS_STORE: &str = "keys";
|
|
||||||
pub const CORE_STORE: &str = "core";
|
|
||||||
|
|
||||||
// keys
|
|
||||||
pub const CONFIG: &str = "config";
|
|
||||||
pub const GATEWAY_DETAILS: &str = "gateway_details";
|
|
||||||
|
|
||||||
pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair";
|
|
||||||
pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair";
|
|
||||||
|
|
||||||
// TODO: for those we could actually use the subtle crypto storage
|
|
||||||
pub const AES128CTR_ACK_KEY: &str = "aes128ctr_ack_key";
|
|
||||||
pub const AES128CTR_BLAKE3_HMAC_GATEWAY_KEYS: &str = "aes128ctr_blake3_hmac_gateway_keys";
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub struct ClientStorage {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) name: String,
|
|
||||||
pub(crate) inner: Arc<WasmStorage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
impl ClientStorage {
|
|
||||||
fn db_name(client_id: &str) -> String {
|
|
||||||
format!("{STORAGE_NAME_PREFIX}-{client_id}")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn new_async(
|
|
||||||
client_id: &str,
|
|
||||||
passphrase: Option<String>,
|
|
||||||
) -> Result<Self, ClientStorageError> {
|
|
||||||
let name = Self::db_name(client_id);
|
|
||||||
|
|
||||||
// make sure the password is zeroized when no longer used, especially if we error out.
|
|
||||||
// special care must be taken on JS side to ensure it's correctly used there.
|
|
||||||
let passphrase = Zeroizing::new(passphrase);
|
|
||||||
|
|
||||||
let migrate_fn = Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
|
|
||||||
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
|
|
||||||
// works with an unsigned integer.
|
|
||||||
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
|
|
||||||
let old_version = evt.old_version() as u32;
|
|
||||||
|
|
||||||
if old_version < 1 {
|
|
||||||
// migrating to version 1
|
|
||||||
let db = evt.db();
|
|
||||||
|
|
||||||
db.create_object_store(v1::KEYS_STORE)?;
|
|
||||||
db.create_object_store(v1::CORE_STORE)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
let inner = WasmStorage::new(
|
|
||||||
&name,
|
|
||||||
STORAGE_VERSION,
|
|
||||||
migrate_fn,
|
|
||||||
passphrase.as_ref().map(|p| p.as_bytes()),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ClientStorage {
|
|
||||||
inner: Arc::new(inner),
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
|
||||||
pub fn new(client_id: String, passphrase: String) -> Promise {
|
|
||||||
future_to_promise(async move {
|
|
||||||
Self::new_async(&client_id, Some(passphrase))
|
|
||||||
.await
|
|
||||||
.into_promise_result()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_unencrypted(client_id: String) -> Promise {
|
|
||||||
future_to_promise(async move {
|
|
||||||
Self::new_async(&client_id, None)
|
|
||||||
.await
|
|
||||||
.into_promise_result()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: persist client's config
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) async fn read_config(&self) -> Result<Option<Config>, ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.read_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG))
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn may_read_gateway_details(
|
|
||||||
&self,
|
|
||||||
) -> Result<Option<PersistedGatewayDetails>, ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_DETAILS))
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn must_read_gateway_details(
|
|
||||||
&self,
|
|
||||||
) -> Result<PersistedGatewayDetails, ClientStorageError> {
|
|
||||||
self.may_read_gateway_details()
|
|
||||||
.await?
|
|
||||||
.ok_or(ClientStorageError::GatewayDetailsNotInStorage)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn may_read_identity_keypair(
|
|
||||||
&self,
|
|
||||||
) -> Result<Option<identity::KeyPair>, ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.read_value(
|
|
||||||
v1::KEYS_STORE,
|
|
||||||
JsValue::from_str(v1::ED25519_IDENTITY_KEYPAIR),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn may_read_encryption_keypair(
|
|
||||||
&self,
|
|
||||||
) -> Result<Option<encryption::KeyPair>, ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.read_value(
|
|
||||||
v1::KEYS_STORE,
|
|
||||||
JsValue::from_str(v1::X25519_ENCRYPTION_KEYPAIR),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn may_read_ack_key(&self) -> Result<Option<AckKey>, ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.read_value(v1::KEYS_STORE, JsValue::from_str(v1::AES128CTR_ACK_KEY))
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn may_read_gateway_shared_key(&self) -> Result<Option<SharedKeys>, ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.read_value(
|
|
||||||
v1::KEYS_STORE,
|
|
||||||
JsValue::from_str(v1::AES128CTR_BLAKE3_HMAC_GATEWAY_KEYS),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn must_read_identity_keypair(&self) -> Result<identity::KeyPair, ClientStorageError> {
|
|
||||||
self.may_read_identity_keypair()
|
|
||||||
.await?
|
|
||||||
.ok_or(ClientStorageError::CryptoKeyNotInStorage {
|
|
||||||
typ: v1::ED25519_IDENTITY_KEYPAIR.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn must_read_encryption_keypair(
|
|
||||||
&self,
|
|
||||||
) -> Result<encryption::KeyPair, ClientStorageError> {
|
|
||||||
self.may_read_encryption_keypair()
|
|
||||||
.await?
|
|
||||||
.ok_or(ClientStorageError::CryptoKeyNotInStorage {
|
|
||||||
typ: v1::X25519_ENCRYPTION_KEYPAIR.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn must_read_ack_key(&self) -> Result<AckKey, ClientStorageError> {
|
|
||||||
self.may_read_ack_key()
|
|
||||||
.await?
|
|
||||||
.ok_or(ClientStorageError::CryptoKeyNotInStorage {
|
|
||||||
typ: v1::AES128CTR_ACK_KEY.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn must_read_gateway_shared_key(&self) -> Result<SharedKeys, ClientStorageError> {
|
|
||||||
self.may_read_gateway_shared_key()
|
|
||||||
.await?
|
|
||||||
.ok_or(ClientStorageError::CryptoKeyNotInStorage {
|
|
||||||
typ: v1::AES128CTR_BLAKE3_HMAC_GATEWAY_KEYS.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_identity_keypair(
|
|
||||||
&self,
|
|
||||||
keypair: &identity::KeyPair,
|
|
||||||
) -> Result<(), ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.store_value(
|
|
||||||
v1::KEYS_STORE,
|
|
||||||
JsValue::from_str(v1::ED25519_IDENTITY_KEYPAIR),
|
|
||||||
keypair,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_encryption_keypair(
|
|
||||||
&self,
|
|
||||||
keypair: &encryption::KeyPair,
|
|
||||||
) -> Result<(), ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.store_value(
|
|
||||||
v1::KEYS_STORE,
|
|
||||||
JsValue::from_str(v1::X25519_ENCRYPTION_KEYPAIR),
|
|
||||||
keypair,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_ack_key(&self, key: &AckKey) -> Result<(), ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.store_value(
|
|
||||||
v1::KEYS_STORE,
|
|
||||||
JsValue::from_str(v1::AES128CTR_ACK_KEY),
|
|
||||||
key,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_gateway_shared_key(&self, key: &SharedKeys) -> Result<(), ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.store_value(
|
|
||||||
v1::KEYS_STORE,
|
|
||||||
JsValue::from_str(v1::AES128CTR_BLAKE3_HMAC_GATEWAY_KEYS),
|
|
||||||
key,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn store_gateway_details(
|
|
||||||
&self,
|
|
||||||
gateway_endpoint: &PersistedGatewayDetails,
|
|
||||||
) -> Result<(), ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.store_value(
|
|
||||||
v1::CORE_STORE,
|
|
||||||
JsValue::from_str(v1::GATEWAY_DETAILS),
|
|
||||||
gateway_endpoint,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: persist client's config
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) async fn store_config(&self, config: &Config) -> Result<(), ClientStorageError> {
|
|
||||||
self.inner
|
|
||||||
.store_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG), config)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn has_full_gateway_info(&self) -> Result<bool, ClientStorageError> {
|
|
||||||
let has_keys = self.may_read_gateway_shared_key().await?.is_some();
|
|
||||||
let has_details = self.may_read_gateway_details().await?.is_some();
|
|
||||||
|
|
||||||
Ok(has_keys && has_details)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::storage::errors::ClientStorageError;
|
|
||||||
use crate::storage::ClientStorage;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
|
||||||
GatewayDetailsStore, PersistedGatewayDetails,
|
|
||||||
};
|
|
||||||
use nym_client_core::client::base_client::storage::MixnetClientStorage;
|
|
||||||
use nym_client_core::client::key_manager::persistence::KeyStore;
|
|
||||||
use nym_client_core::client::key_manager::KeyManager;
|
|
||||||
use nym_client_core::client::replies::reply_storage::browser_backend;
|
|
||||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
|
||||||
use wasm_utils::console_log;
|
|
||||||
|
|
||||||
// temporary until other variants are properly implemented (probably it should get changed into `ClientStorage`
|
|
||||||
// implementing all traits and everything getting combined
|
|
||||||
pub struct FullWasmClientStorage {
|
|
||||||
pub(crate) keys_and_gateway_store: ClientStorage,
|
|
||||||
pub(crate) reply_storage: browser_backend::Backend,
|
|
||||||
pub(crate) credential_storage: EphemeralCredentialStorage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MixnetClientStorage for FullWasmClientStorage {
|
|
||||||
type KeyStore = ClientStorage;
|
|
||||||
type ReplyStore = browser_backend::Backend;
|
|
||||||
type CredentialStore = EphemeralCredentialStorage;
|
|
||||||
|
|
||||||
type GatewayDetailsStore = ClientStorage;
|
|
||||||
|
|
||||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
|
||||||
(self.reply_storage, self.credential_storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_store(&self) -> &Self::KeyStore {
|
|
||||||
&self.keys_and_gateway_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reply_store(&self) -> &Self::ReplyStore {
|
|
||||||
&self.reply_storage
|
|
||||||
}
|
|
||||||
|
|
||||||
fn credential_store(&self) -> &Self::CredentialStore {
|
|
||||||
&self.credential_storage
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
|
||||||
&self.keys_and_gateway_store
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl KeyStore for ClientStorage {
|
|
||||||
type StorageError = ClientStorageError;
|
|
||||||
|
|
||||||
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
|
|
||||||
console_log!("attempting to load cryptographic keys...");
|
|
||||||
|
|
||||||
// all keys implement `ZeroizeOnDrop`, so if we return an Error, whatever was already loaded will be cleared
|
|
||||||
let identity_keypair = self.must_read_identity_keypair().await?;
|
|
||||||
let encryption_keypair = self.must_read_encryption_keypair().await?;
|
|
||||||
let ack_keypair = self.must_read_ack_key().await?;
|
|
||||||
let gateway_shared_key = self.must_read_gateway_shared_key().await?;
|
|
||||||
|
|
||||||
Ok(KeyManager::from_keys(
|
|
||||||
identity_keypair,
|
|
||||||
encryption_keypair,
|
|
||||||
gateway_shared_key,
|
|
||||||
ack_keypair,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
|
|
||||||
console_log!("attempting to store cryptographic keys...");
|
|
||||||
|
|
||||||
self.store_identity_keypair(&keys.identity_keypair())
|
|
||||||
.await?;
|
|
||||||
self.store_encryption_keypair(&keys.encryption_keypair())
|
|
||||||
.await?;
|
|
||||||
self.store_ack_key(&keys.ack_key()).await?;
|
|
||||||
self.store_gateway_shared_key(&keys.gateway_shared_key())
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl GatewayDetailsStore for ClientStorage {
|
|
||||||
type StorageError = ClientStorageError;
|
|
||||||
|
|
||||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
|
||||||
self.must_read_gateway_details().await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_gateway_details(
|
|
||||||
&self,
|
|
||||||
details: &PersistedGatewayDetails,
|
|
||||||
) -> Result<(), Self::StorageError> {
|
|
||||||
self.store_gateway_details(details).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::tester::helpers::{NodeTestResult, WasmTestMessageExt};
|
use crate::tester::helpers::NodeTestResult;
|
||||||
|
use crate::tester::NodeTestMessage;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use nym_node_tester_utils::processor::Received;
|
use nym_node_tester_utils::receiver::{Received, ReceivedReceiver};
|
||||||
use nym_node_tester_utils::receiver::ReceivedReceiver;
|
|
||||||
use nym_sphinx::chunking::fragment::FragmentIdentifier;
|
use nym_sphinx::chunking::fragment::FragmentIdentifier;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -21,7 +21,7 @@ pub(crate) struct EphemeralTestReceiver<'a> {
|
|||||||
duplicate_acks: u32,
|
duplicate_acks: u32,
|
||||||
|
|
||||||
timeout_duration: Duration,
|
timeout_duration: Duration,
|
||||||
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver<WasmTestMessageExt>>,
|
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EphemeralTestReceiver<'a> {
|
impl<'a> EphemeralTestReceiver<'a> {
|
||||||
@@ -38,7 +38,7 @@ impl<'a> EphemeralTestReceiver<'a> {
|
|||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
sent_packets: u32,
|
sent_packets: u32,
|
||||||
expected_acks: HashSet<FragmentIdentifier>,
|
expected_acks: HashSet<FragmentIdentifier>,
|
||||||
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver<WasmTestMessageExt>>,
|
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver>,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
EphemeralTestReceiver {
|
EphemeralTestReceiver {
|
||||||
@@ -53,18 +53,23 @@ impl<'a> EphemeralTestReceiver<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_next_received_packet(&mut self, packet: Option<Received<WasmTestMessageExt>>) -> bool {
|
fn on_next_received_packet(&mut self, packet: Option<Received>) -> bool {
|
||||||
let Some(received_packet) = packet else {
|
let Some(received_packet) = packet else {
|
||||||
// can't do anything more...
|
// can't do anything more...
|
||||||
console_error!("packet receiver has stopped processing results!");
|
console_error!("packet receiver has stopped processing results!");
|
||||||
return true
|
return true
|
||||||
};
|
};
|
||||||
match received_packet {
|
match received_packet {
|
||||||
Received::Message(msg) => {
|
Received::Message(msg) => match NodeTestMessage::try_recover(msg) {
|
||||||
if !self.received_valid_messages.insert(msg.msg_id) {
|
Ok(test_msg) => {
|
||||||
self.duplicate_packets += 1;
|
if !self.received_valid_messages.insert(test_msg.msg_id) {
|
||||||
|
self.duplicate_packets += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Err(err) => {
|
||||||
|
console_warn!("failed to recover test message from received packet: {err}")
|
||||||
|
}
|
||||||
|
},
|
||||||
Received::Ack(frag_id) => {
|
Received::Ack(frag_id) => {
|
||||||
if self.expected_acks.contains(&frag_id) {
|
if self.expected_acks.contains(&frag_id) {
|
||||||
if !self.received_valid_acks.insert(frag_id) {
|
if !self.received_valid_acks.insert(frag_id) {
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
|
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
|
||||||
#![allow(clippy::drop_non_drop)]
|
#![allow(clippy::drop_non_drop)]
|
||||||
|
|
||||||
use nym_node_tester_utils::processor::Received;
|
use nym_node_tester_utils::receiver::{Received, ReceivedReceiver};
|
||||||
use nym_node_tester_utils::receiver::ReceivedReceiver;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
@@ -15,10 +14,10 @@ use wasm_bindgen::prelude::*;
|
|||||||
use wasm_utils::{console_log, console_warn};
|
use wasm_utils::{console_log, console_warn};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct ReceivedReceiverWrapper(Arc<AsyncMutex<ReceivedReceiver<WasmTestMessageExt>>>);
|
pub(super) struct ReceivedReceiverWrapper(Arc<AsyncMutex<ReceivedReceiver>>);
|
||||||
|
|
||||||
impl ReceivedReceiverWrapper {
|
impl ReceivedReceiverWrapper {
|
||||||
pub(super) fn new(inner: ReceivedReceiver<WasmTestMessageExt>) -> Self {
|
pub(super) fn new(inner: ReceivedReceiver) -> Self {
|
||||||
ReceivedReceiverWrapper(Arc::new(AsyncMutex::new(inner)))
|
ReceivedReceiverWrapper(Arc::new(AsyncMutex::new(inner)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ impl ReceivedReceiverWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn lock(&self) -> AsyncMutexGuard<'_, ReceivedReceiver<WasmTestMessageExt>> {
|
pub(super) async fn lock(&self) -> AsyncMutexGuard<'_, ReceivedReceiver> {
|
||||||
self.0.lock().await
|
self.0.lock().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::constants::NODE_TESTER_ID;
|
|
||||||
use crate::error::WasmClientError;
|
use crate::error::WasmClientError;
|
||||||
use crate::helpers::{current_network_topology_async, setup_from_topology};
|
use crate::helpers::{current_network_topology_async, setup_new_key_manager};
|
||||||
use crate::storage::ClientStorage;
|
|
||||||
use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
|
use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
|
||||||
use crate::tester::helpers::{
|
use crate::tester::helpers::{
|
||||||
NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
|
NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
|
||||||
@@ -14,9 +12,10 @@ use futures::channel::mpsc;
|
|||||||
use js_sys::Promise;
|
use js_sys::Promise;
|
||||||
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
|
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
|
||||||
use nym_bandwidth_controller::BandwidthController;
|
use nym_bandwidth_controller::BandwidthController;
|
||||||
use nym_client_core::client::key_manager::ManagedKeys;
|
use nym_client_core::client::key_manager::KeyManager;
|
||||||
use nym_client_core::init::InitialisationDetails;
|
use nym_client_core::config::GatewayEndpointConfig;
|
||||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
|
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
|
||||||
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_gateway_client::GatewayClient;
|
use nym_gateway_client::GatewayClient;
|
||||||
use nym_node_tester_utils::receiver::SimpleMessageReceiver;
|
use nym_node_tester_utils::receiver::SimpleMessageReceiver;
|
||||||
use nym_node_tester_utils::{NodeTester, TestMessage};
|
use nym_node_tester_utils::{NodeTester, TestMessage};
|
||||||
@@ -26,7 +25,6 @@ use nym_sphinx::params::PacketSize;
|
|||||||
use nym_sphinx::preparer::PreparedFragment;
|
use nym_sphinx::preparer::PreparedFragment;
|
||||||
use nym_task::TaskManager;
|
use nym_task::TaskManager;
|
||||||
use nym_topology::NymTopology;
|
use nym_topology::NymTopology;
|
||||||
use nym_validator_client::client::IdentityKey;
|
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||||
@@ -35,7 +33,7 @@ use std::time::Duration;
|
|||||||
use tokio::sync::Mutex as AsyncMutex;
|
use tokio::sync::Mutex as AsyncMutex;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen_futures::future_to_promise;
|
use wasm_bindgen_futures::future_to_promise;
|
||||||
use wasm_utils::{check_promise_result, console_log, PromisableResult};
|
use wasm_utils::{check_promise_result, console_log, console_warn, PromisableResult};
|
||||||
|
|
||||||
mod ephemeral_receiver;
|
mod ephemeral_receiver;
|
||||||
pub(crate) mod helpers;
|
pub(crate) mod helpers;
|
||||||
@@ -72,19 +70,22 @@ pub struct NymNodeTester {
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct NymNodeTesterBuilder {
|
pub struct NymNodeTesterBuilder {
|
||||||
gateway: Option<IdentityKey>,
|
gateway_config: GatewayEndpointConfig,
|
||||||
|
|
||||||
base_topology: NymTopology,
|
base_topology: NymTopology,
|
||||||
|
|
||||||
|
/// KeyManager object containing smart pointers to all relevant keys used by the client.
|
||||||
|
key_manager: KeyManager,
|
||||||
|
|
||||||
// unimplemented
|
// unimplemented
|
||||||
bandwidth_controller:
|
bandwidth_controller:
|
||||||
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
|
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
|
fn address(keys: &KeyManager, gateway_identity: NodeIdentity) -> Recipient {
|
||||||
Recipient::new(
|
Recipient::new(
|
||||||
*keys.identity_public_key(),
|
*keys.identity_keypair().public_key(),
|
||||||
*keys.encryption_public_key(),
|
*keys.encryption_keypair().public_key(),
|
||||||
gateway_identity,
|
gateway_identity,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -93,61 +94,57 @@ fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
|
|||||||
impl NymNodeTesterBuilder {
|
impl NymNodeTesterBuilder {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
gateway_config: GatewayEndpointConfig,
|
||||||
base_topology: WasmNymTopology,
|
base_topology: WasmNymTopology,
|
||||||
gateway: Option<IdentityKey>,
|
|
||||||
) -> NymNodeTesterBuilder {
|
) -> NymNodeTesterBuilder {
|
||||||
NymNodeTesterBuilder {
|
NymNodeTesterBuilder {
|
||||||
gateway,
|
gateway_config,
|
||||||
base_topology: base_topology.into(),
|
base_topology: base_topology.into(),
|
||||||
|
key_manager: setup_new_key_manager(),
|
||||||
bandwidth_controller: None,
|
bandwidth_controller: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn _new_with_api(
|
async fn _new_with_api(
|
||||||
|
gateway_config: GatewayEndpointConfig,
|
||||||
api_url: String,
|
api_url: String,
|
||||||
gateway: Option<IdentityKey>,
|
|
||||||
) -> Result<Self, WasmClientError> {
|
) -> Result<Self, WasmClientError> {
|
||||||
let topology = current_network_topology_async(api_url).await?;
|
let topology = current_network_topology_async(api_url).await?;
|
||||||
Ok(NymNodeTesterBuilder::new(topology, gateway))
|
Ok(NymNodeTesterBuilder::new(gateway_config, topology))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_api(gateway: Option<IdentityKey>, api_url: String) -> Promise {
|
pub fn new_with_api(gateway_config: GatewayEndpointConfig, api_url: String) -> Promise {
|
||||||
future_to_promise(async move {
|
future_to_promise(async move {
|
||||||
Self::_new_with_api(api_url, gateway)
|
Self::_new_with_api(gateway_config, api_url)
|
||||||
.await
|
.await
|
||||||
.into_promise_result()
|
.into_promise_result()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn gateway_info(
|
|
||||||
&self,
|
|
||||||
client_store: &ClientStorage,
|
|
||||||
) -> Result<InitialisationDetails, WasmClientError> {
|
|
||||||
if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await {
|
|
||||||
Ok(loaded)
|
|
||||||
} else {
|
|
||||||
setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
|
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
|
||||||
|
let rng = OsRng;
|
||||||
let task_manager = TaskManager::default();
|
let task_manager = TaskManager::default();
|
||||||
|
|
||||||
let client_store = ClientStorage::new_async(NODE_TESTER_ID, None).await?;
|
let gateway_identity =
|
||||||
|
identity::PublicKey::from_base58_string(self.gateway_config.gateway_id)
|
||||||
|
.map_err(|source| WasmClientError::InvalidGatewayIdentity { source })?;
|
||||||
|
|
||||||
let init_details = self.gateway_info(&client_store).await?;
|
// we **REALLY** need persistence...
|
||||||
let gateway_endpoint = init_details.gateway_details;
|
let shared_key = if self.key_manager.is_gateway_key_set() {
|
||||||
let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?;
|
Some(self.key_manager.gateway_shared_key())
|
||||||
let managed_keys = init_details.managed_keys;
|
} else {
|
||||||
|
console_warn!("Gateway key not set - will derive a fresh one.");
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded();
|
let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded();
|
||||||
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
||||||
|
|
||||||
let mut gateway_client = GatewayClient::new(
|
let mut gateway_client = GatewayClient::new(
|
||||||
gateway_endpoint.gateway_listener,
|
self.gateway_config.gateway_listener,
|
||||||
managed_keys.identity_keypair(),
|
self.key_manager.identity_keypair(),
|
||||||
gateway_identity,
|
gateway_identity,
|
||||||
Some(managed_keys.must_get_gateway_shared_key()),
|
shared_key,
|
||||||
mixnet_message_sender,
|
mixnet_message_sender,
|
||||||
ack_sender,
|
ack_sender,
|
||||||
Duration::from_secs(10),
|
Duration::from_secs(10),
|
||||||
@@ -156,24 +153,27 @@ impl NymNodeTesterBuilder {
|
|||||||
);
|
);
|
||||||
|
|
||||||
gateway_client.set_disabled_credentials_mode(true);
|
gateway_client.set_disabled_credentials_mode(true);
|
||||||
gateway_client.authenticate_and_start().await?;
|
let shared_keys = gateway_client.authenticate_and_start().await?;
|
||||||
|
|
||||||
|
// currently pointless but might as well do it for the future ¯\_(ツ)_/¯
|
||||||
|
self.key_manager.insert_gateway_shared_key(shared_keys);
|
||||||
|
|
||||||
// TODO: make those values configurable later
|
// TODO: make those values configurable later
|
||||||
let tester = NodeTester::new(
|
let tester = NodeTester::new(
|
||||||
OsRng,
|
rng,
|
||||||
self.base_topology,
|
self.base_topology,
|
||||||
Some(address(&managed_keys, gateway_identity)),
|
address(&self.key_manager, gateway_identity),
|
||||||
PacketSize::default(),
|
PacketSize::default(),
|
||||||
Duration::from_millis(5),
|
Duration::from_millis(5),
|
||||||
Duration::from_millis(5),
|
Duration::from_millis(5),
|
||||||
managed_keys.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (processed_sender, processed_receiver) = mpsc::unbounded();
|
let (processed_sender, processed_receiver) = mpsc::unbounded();
|
||||||
|
|
||||||
let mut receiver = SimpleMessageReceiver::new_sphinx_receiver(
|
let mut receiver = SimpleMessageReceiver::new_sphinx_receiver(
|
||||||
managed_keys.encryption_keypair(),
|
self.key_manager.encryption_keypair(),
|
||||||
managed_keys.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
mixnet_message_receiver,
|
mixnet_message_receiver,
|
||||||
ack_receiver,
|
ack_receiver,
|
||||||
processed_sender,
|
processed_sender,
|
||||||
@@ -234,24 +234,24 @@ async fn test_mixnode(
|
|||||||
impl NymNodeTester {
|
impl NymNodeTester {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(topology: WasmNymTopology, gateway: Option<IdentityKey>) -> Promise {
|
pub fn new(gateway_config: GatewayEndpointConfig, topology: WasmNymTopology) -> Promise {
|
||||||
console_log!("constructing node tester!");
|
console_log!("constructing node tester!");
|
||||||
NymNodeTesterBuilder::new(topology, gateway).setup_client()
|
NymNodeTesterBuilder::new(gateway_config, topology).setup_client()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn _new_with_api(
|
async fn _new_with_api(
|
||||||
|
gateway_config: GatewayEndpointConfig,
|
||||||
api_url: String,
|
api_url: String,
|
||||||
gateway: Option<IdentityKey>,
|
|
||||||
) -> Result<Self, WasmClientError> {
|
) -> Result<Self, WasmClientError> {
|
||||||
NymNodeTesterBuilder::_new_with_api(api_url, gateway)
|
NymNodeTesterBuilder::_new_with_api(gateway_config, api_url)
|
||||||
.await?
|
.await?
|
||||||
._setup_client()
|
._setup_client()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_api(api_url: String, gateway: Option<IdentityKey>) -> Promise {
|
pub fn new_with_api(gateway_config: GatewayEndpointConfig, api_url: String) -> Promise {
|
||||||
future_to_promise(async move {
|
future_to_promise(async move {
|
||||||
Self::_new_with_api(api_url, gateway)
|
Self::_new_with_api(gateway_config, api_url)
|
||||||
.await
|
.await
|
||||||
.into_promise_result()
|
.into_promise_result()
|
||||||
})
|
})
|
||||||
@@ -266,12 +266,7 @@ impl NymNodeTester {
|
|||||||
let test_ext = WasmTestMessageExt::new(test_nonce);
|
let test_ext = WasmTestMessageExt::new(test_nonce);
|
||||||
let mut tester_permit = self.tester.lock().expect("mutex got poisoned");
|
let mut tester_permit = self.tester.lock().expect("mutex got poisoned");
|
||||||
tester_permit
|
tester_permit
|
||||||
.existing_identity_mixnode_test_packets(
|
.existing_identity_mixnode_test_packets(mixnode_identity, test_ext, num_test_packets)
|
||||||
mixnode_identity,
|
|
||||||
test_ext,
|
|
||||||
num_test_packets,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use nym_crypto::asymmetric::{encryption, identity};
|
|||||||
use nym_topology::gateway::GatewayConversionError;
|
use nym_topology::gateway::GatewayConversionError;
|
||||||
use nym_topology::mix::{Layer, MixnodeConversionError};
|
use nym_topology::mix::{Layer, MixnodeConversionError};
|
||||||
use nym_topology::{gateway, mix, MixLayer, NymTopology};
|
use nym_topology::{gateway, mix, MixLayer, NymTopology};
|
||||||
use nym_validator_client::client::{IdentityKeyRef, MixId};
|
use nym_validator_client::client::MixId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -82,16 +82,11 @@ impl WasmNymTopology {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn ensure_contains(&self, gateway_config: &GatewayEndpointConfig) -> bool {
|
pub(crate) fn ensure_contains(&self, gateway_config: &GatewayEndpointConfig) -> bool {
|
||||||
self.ensure_contains_gateway_id(&gateway_config.gateway_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn ensure_contains_gateway_id(&self, gateway_id: IdentityKeyRef) -> bool {
|
|
||||||
self.inner
|
self.inner
|
||||||
.gateways()
|
.gateways()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|g| g.identity_key.to_base58_string() == gateway_id)
|
.any(|g| g.identity_key.to_base58_string() == gateway_config.gateway_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(&self) {
|
pub fn print(&self) {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ impl AsyncFileWatcher {
|
|||||||
Ok(event) => {
|
Ok(event) => {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if self.should_propagate(&event, now) {
|
if self.should_propagate(&event, now) {
|
||||||
self.last_received.insert(event.kind, now);
|
self.last_received.insert(event.kind.clone(), now);
|
||||||
if let Err(_err) = self.event_sender.unbounded_send(event) {
|
if let Err(_err) = self.event_sender.unbounded_send(event) {
|
||||||
log::error!("the file watcher receiver has been dropped!");
|
log::error!("the file watcher receiver has been dropped!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,16 +55,11 @@ where
|
|||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_credential<C, St>(
|
pub async fn get_credential<C: DkgQueryClient + Send + Sync, St: Storage>(
|
||||||
state: &State,
|
state: &State,
|
||||||
client: &C,
|
client: &C,
|
||||||
storage: &St,
|
storage: &St,
|
||||||
) -> Result<(), BandwidthControllerError>
|
) -> Result<(), BandwidthControllerError> {
|
||||||
where
|
|
||||||
C: DkgQueryClient + Send + Sync,
|
|
||||||
St: Storage,
|
|
||||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let epoch_id = client.get_current_epoch().await?.epoch_id;
|
let epoch_id = client.get_current_epoch().await?.epoch_id;
|
||||||
let threshold = client
|
let threshold = client
|
||||||
.get_current_epoch_threshold()
|
.get_current_epoch_threshold()
|
||||||
@@ -88,6 +83,7 @@ where
|
|||||||
signature.to_bs58(),
|
signature.to_bs58(),
|
||||||
epoch_id.to_string(),
|
epoch_id.to_string(),
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,7 @@ pub enum BandwidthControllerError {
|
|||||||
Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError),
|
Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError),
|
||||||
|
|
||||||
#[error("There was a credential storage error - {0}")]
|
#[error("There was a credential storage error - {0}")]
|
||||||
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
|
CredentialStorageError(#[from] StorageError),
|
||||||
|
|
||||||
// this should really be fully incorporated into the above, but messing with coconut is the last thing I want to do now
|
|
||||||
#[error(transparent)]
|
|
||||||
StorageError(#[from] StorageError),
|
|
||||||
|
|
||||||
#[error("Coconut error - {0}")]
|
#[error("Coconut error - {0}")]
|
||||||
CoconutError(#[from] CoconutError),
|
CoconutError(#[from] CoconutError),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub mod error;
|
|||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub mod wasm_mockups;
|
pub mod wasm_mockups;
|
||||||
|
|
||||||
pub struct BandwidthController<C, St> {
|
pub struct BandwidthController<C, St: Storage> {
|
||||||
storage: St,
|
storage: St,
|
||||||
client: C,
|
client: C,
|
||||||
}
|
}
|
||||||
@@ -45,13 +45,8 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
|||||||
) -> Result<(nym_coconut_interface::Credential, i64), BandwidthControllerError>
|
) -> Result<(nym_coconut_interface::Credential, i64), BandwidthControllerError>
|
||||||
where
|
where
|
||||||
C: DkgQueryClient + Sync + Send,
|
C: DkgQueryClient + Sync + Send,
|
||||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
|
||||||
{
|
{
|
||||||
let bandwidth_credential = self
|
let bandwidth_credential = self.storage.get_next_coconut_credential().await?;
|
||||||
.storage
|
|
||||||
.get_next_coconut_credential()
|
|
||||||
.await
|
|
||||||
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?;
|
|
||||||
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
|
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
|
||||||
.map_err(|_| StorageError::InconsistentData)?;
|
.map_err(|_| StorageError::InconsistentData)?;
|
||||||
let voucher_info = bandwidth_credential.voucher_info.clone();
|
let voucher_info = bandwidth_credential.voucher_info.clone();
|
||||||
@@ -87,16 +82,10 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn consume_credential(&self, id: i64) -> Result<(), BandwidthControllerError>
|
pub async fn consume_credential(&self, id: i64) -> Result<(), BandwidthControllerError> {
|
||||||
where
|
|
||||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
// JS: shouldn't we send some contract/validator/gateway message here to actually, you know,
|
// JS: shouldn't we send some contract/validator/gateway message here to actually, you know,
|
||||||
// consume it?
|
// consume it?
|
||||||
self.storage
|
Ok(self.storage.consume_coconut_credential(id).await?)
|
||||||
.consume_coconut_credential(id)
|
|
||||||
.await
|
|
||||||
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-bin-common"
|
name = "nym-bin-common"
|
||||||
version = "0.6.0"
|
version = "0.5.0"
|
||||||
description = "Common code for nym binaries"
|
description = "Common code for nym binaries"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
@@ -15,21 +15,15 @@ clap_complete_fig = "4.0"
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
semver = "0.11"
|
semver = "0.11"
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"], optional = true }
|
||||||
serde_json = { workspace = true, optional = true }
|
serde_json = { workspace = true, optional = true }
|
||||||
|
|
||||||
## tracing
|
## tracing
|
||||||
|
tracing-appender = { version = "0.2.2", optional = true }
|
||||||
tracing-subscriber = { version = "0.3.16", features = [
|
tracing-subscriber = { version = "0.3.16", features = [
|
||||||
"env-filter",
|
"env-filter",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
tracing-tree = { version = "0.2.2", optional = true }
|
tracing-tree = { version = "0.2.2", optional = true }
|
||||||
opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
|
|
||||||
"rt-tokio",
|
|
||||||
"collector_client",
|
|
||||||
"isahc_collector_client",
|
|
||||||
] }
|
|
||||||
tracing-opentelemetry = { version = "0.19.0", optional = true }
|
|
||||||
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
|
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
@@ -42,11 +36,5 @@ vergen = { version = "=7.4.3", default-features = false, features = [
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
output_format = ["serde_json"]
|
output_format = ["serde", "serde_json"]
|
||||||
tracing = [
|
tracing = ["tracing-appender", "tracing-subscriber", "tracing-tree"]
|
||||||
"tracing-subscriber",
|
|
||||||
"tracing-tree",
|
|
||||||
"opentelemetry-jaeger",
|
|
||||||
"tracing-opentelemetry",
|
|
||||||
"opentelemetry",
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
// TODO: at a later date this crate should probably also expose `ContractBuildInformation`
|
// TODO: at a later date this crate should probably also expose `ContractBuildInformation`
|
||||||
// and be used by our smart contracts
|
// and be used by our smart contracts
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BinaryBuildInformation {
|
pub struct BinaryBuildInformation {
|
||||||
// VERGEN_BUILD_TIMESTAMP
|
// VERGEN_BUILD_TIMESTAMP
|
||||||
@@ -101,7 +99,8 @@ impl BinaryBuildInformation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct BinaryBuildInformationOwned {
|
pub struct BinaryBuildInformationOwned {
|
||||||
// VERGEN_BUILD_TIMESTAMP
|
// VERGEN_BUILD_TIMESTAMP
|
||||||
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
|
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
|
||||||
|
|||||||
@@ -4,7 +4,5 @@
|
|||||||
pub mod build_information;
|
pub mod build_information;
|
||||||
pub mod completions;
|
pub mod completions;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
pub mod version_checker;
|
|
||||||
|
|
||||||
#[cfg(feature = "output_format")]
|
|
||||||
pub mod output_format;
|
pub mod output_format;
|
||||||
|
pub mod version_checker;
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// use tracing_subscriber::{
|
||||||
use serde::{Deserialize, Serialize};
|
// fmt::Layer, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry,
|
||||||
|
// };
|
||||||
|
// use tracing_tree::HierarchicalLayer;
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
pub use opentelemetry;
|
pub use tracing_appender;
|
||||||
#[cfg(feature = "tracing")]
|
|
||||||
pub use opentelemetry_jaeger;
|
|
||||||
#[cfg(feature = "tracing")]
|
|
||||||
pub use tracing_opentelemetry;
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
pub use tracing_subscriber;
|
pub use tracing_subscriber;
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
pub use tracing_tree;
|
pub use tracing_tree;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct LoggingSettings {
|
|
||||||
// well, we need to implement something here at some point...
|
|
||||||
}
|
|
||||||
|
|
||||||
// I'd argue we should start transitioning from `log` to `tracing`
|
// I'd argue we should start transitioning from `log` to `tracing`
|
||||||
pub fn setup_logging() {
|
pub fn setup_logging() {
|
||||||
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
||||||
@@ -40,8 +32,6 @@ pub fn setup_logging() {
|
|||||||
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
|
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
|
||||||
.filter_module("handlebars", log::LevelFilter::Warn)
|
.filter_module("handlebars", log::LevelFilter::Warn)
|
||||||
.filter_module("sled", log::LevelFilter::Warn)
|
.filter_module("sled", log::LevelFilter::Warn)
|
||||||
.filter_module("quinn", log::LevelFilter::Warn)
|
|
||||||
.filter_module("rustls", log::LevelFilter::Warn)
|
|
||||||
.init();
|
.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,35 +39,27 @@ pub fn setup_logging() {
|
|||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! setup_tracing {
|
macro_rules! setup_tracing {
|
||||||
($service_name: expr) => {
|
($file_name: expr) => {
|
||||||
use nym_bin_common::logging::tracing_subscriber::layer::SubscriberExt;
|
use nym_bin_common::logging::tracing_subscriber::layer::SubscriberExt;
|
||||||
use nym_bin_common::logging::tracing_subscriber::util::SubscriberInitExt;
|
use nym_bin_common::logging::tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
|
||||||
let registry = nym_bin_common::logging::tracing_subscriber::Registry::default()
|
let file_appender =
|
||||||
|
nym_bin_common::logging::tracing_appender::rolling::hourly($file_name, "log");
|
||||||
|
let (non_blocking, _guard) =
|
||||||
|
nym_bin_common::logging::tracing_appender::non_blocking(file_appender);
|
||||||
|
let appender_layer = nym_bin_common::logging::tracing_subscriber::fmt::Layer::new()
|
||||||
|
.with_ansi(false)
|
||||||
|
.with_writer(non_blocking);
|
||||||
|
|
||||||
|
nym_bin_common::logging::tracing_subscriber::Registry::default()
|
||||||
.with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env())
|
.with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env())
|
||||||
|
.with(appender_layer)
|
||||||
.with(
|
.with(
|
||||||
nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4)
|
nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4)
|
||||||
.with_targets(true)
|
.with_targets(true)
|
||||||
.with_bracketed_fields(true),
|
.with_bracketed_fields(true),
|
||||||
);
|
|
||||||
|
|
||||||
let tracer = nym_bin_common::logging::opentelemetry_jaeger::new_collector_pipeline()
|
|
||||||
.with_endpoint("http://44.199.230.10:14268/api/traces")
|
|
||||||
.with_service_name($service_name)
|
|
||||||
.with_isahc()
|
|
||||||
.with_trace_config(
|
|
||||||
nym_bin_common::logging::opentelemetry::sdk::trace::config().with_sampler(
|
|
||||||
nym_bin_common::logging::opentelemetry::sdk::trace::Sampler::TraceIdRatioBased(
|
|
||||||
0.1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.install_batch(nym_bin_common::logging::opentelemetry::runtime::Tokio)
|
.init();
|
||||||
.expect("Could not init tracer");
|
|
||||||
|
|
||||||
let telemetry = nym_bin_common::logging::tracing_opentelemetry::layer().with_tracer(tracer);
|
|
||||||
|
|
||||||
registry.with(telemetry).init();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client-core"
|
name = "nym-client-core"
|
||||||
version = "1.1.15"
|
version = "1.1.14"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.66"
|
rust-version = "1.66"
|
||||||
@@ -9,7 +9,6 @@ rust-version = "1.66"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
base64 = "0.21.2"
|
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
dashmap = "5.4.0"
|
dashmap = "5.4.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
@@ -18,14 +17,12 @@ log = { workspace = true }
|
|||||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
sha2 = "0.10.6"
|
|
||||||
tap = "1.0.1"
|
tap = "1.0.1"
|
||||||
thiserror = "1.0.34"
|
thiserror = "1.0.34"
|
||||||
url = { version ="2.2", features = ["serde"] }
|
url = { version ="2.2", features = ["serde"] }
|
||||||
tungstenite = { version = "0.13.0", default-features = false }
|
tungstenite = { version = "0.13.0", default-features = false }
|
||||||
tokio = { version = "1.24.1", features = ["macros"]}
|
tokio = { version = "1.24.1", features = ["macros"]}
|
||||||
time = "0.3.17"
|
time = "0.3.17"
|
||||||
zeroize = { workspace = true }
|
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
nym-bandwidth-controller = { path = "../bandwidth-controller" }
|
nym-bandwidth-controller = { path = "../bandwidth-controller" }
|
||||||
@@ -41,7 +38,6 @@ nym-topology = { path = "../topology" }
|
|||||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||||
nym-task = { path = "../task" }
|
nym-task = { path = "../task" }
|
||||||
nym-credential-storage = { path = "../credential-storage" }
|
nym-credential-storage = { path = "../credential-storage" }
|
||||||
nym-network-defaults = { path = "../network-defaults" }
|
|
||||||
|
|
||||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
|
||||||
path = "../client-libs/validator-client"
|
path = "../client-libs/validator-client"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
use crate::{client::replies::reply_storage, config::DebugConfig};
|
use crate::{client::replies::reply_storage, config::DebugConfig};
|
||||||
|
|
||||||
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
|
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
|
||||||
|
|||||||
@@ -2,11 +2,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use super::received_buffer::ReceivedBufferMessage;
|
use super::received_buffer::ReceivedBufferMessage;
|
||||||
use crate::client::base_client::storage::MixnetClientStorage;
|
|
||||||
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
|
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
|
||||||
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
|
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
|
||||||
use crate::client::key_manager::persistence::KeyStore;
|
use crate::client::key_manager::KeyManager;
|
||||||
use crate::client::key_manager::ManagedKeys;
|
|
||||||
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
|
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
|
||||||
use crate::client::real_messages_control;
|
use crate::client::real_messages_control;
|
||||||
use crate::client::real_messages_control::RealMessagesController;
|
use crate::client::real_messages_control::RealMessagesController;
|
||||||
@@ -28,7 +26,6 @@ use crate::{config, spawn_future};
|
|||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use nym_bandwidth_controller::BandwidthController;
|
use nym_bandwidth_controller::BandwidthController;
|
||||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
use nym_crypto::asymmetric::{encryption, identity};
|
||||||
use nym_gateway_client::{
|
use nym_gateway_client::{
|
||||||
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
||||||
@@ -37,7 +34,6 @@ use nym_gateway_client::{
|
|||||||
use nym_sphinx::acknowledgements::AckKey;
|
use nym_sphinx::acknowledgements::AckKey;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::addressing::nodes::NodeIdentity;
|
use nym_sphinx::addressing::nodes::NodeIdentity;
|
||||||
use nym_sphinx::params::PacketType;
|
|
||||||
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
|
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
|
||||||
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
|
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
|
||||||
use nym_task::{TaskClient, TaskManager};
|
use nym_task::{TaskClient, TaskManager};
|
||||||
@@ -46,19 +42,17 @@ use std::sync::Arc;
|
|||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
use nym_credential_storage::storage::Storage;
|
||||||
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
|
|
||||||
|
|
||||||
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
|
|
||||||
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails};
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
|
||||||
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||||
pub mod non_wasm_helpers;
|
pub mod non_wasm_helpers;
|
||||||
|
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod storage;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ClientInput {
|
pub struct ClientInput {
|
||||||
@@ -157,58 +151,76 @@ impl From<bool> for CredentialsToggle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
|
pub struct BaseClientBuilder<'a, B, C, St: Storage> {
|
||||||
config: &'a Config,
|
// due to wasm limitations I had to split it like this : (
|
||||||
client_store: S,
|
gateway_config: &'a GatewayEndpointConfig,
|
||||||
dkg_query_client: Option<C>,
|
debug_config: &'a DebugConfig,
|
||||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
disabled_credentials: bool,
|
||||||
setup_method: GatewaySetup,
|
nym_api_endpoints: Vec<Url>,
|
||||||
|
reply_storage_backend: B,
|
||||||
|
|
||||||
|
custom_topology_provider: Option<Box<dyn TopologyProvider>>,
|
||||||
|
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||||
|
key_manager: KeyManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, S> BaseClientBuilder<'a, C, S>
|
impl<'a, B, C, St> BaseClientBuilder<'a, B, C, St>
|
||||||
where
|
where
|
||||||
S: MixnetClientStorage + 'static,
|
B: ReplyStorageBackend + Send + Sync + 'static,
|
||||||
C: DkgQueryClient + Send + Sync + 'static,
|
C: DkgQueryClient + Sync + Send + 'static,
|
||||||
|
St: Storage + 'static,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new_from_base_config<T>(
|
||||||
base_config: &'a Config,
|
base_config: &'a Config<T>,
|
||||||
client_store: S,
|
key_manager: KeyManager,
|
||||||
dkg_query_client: Option<C>,
|
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||||
) -> BaseClientBuilder<'a, C, S> {
|
reply_storage_backend: B,
|
||||||
|
) -> BaseClientBuilder<'a, B, C, St> {
|
||||||
BaseClientBuilder {
|
BaseClientBuilder {
|
||||||
config: base_config,
|
gateway_config: base_config.get_gateway_endpoint_config(),
|
||||||
client_store,
|
debug_config: base_config.get_debug_config(),
|
||||||
dkg_query_client,
|
disabled_credentials: base_config.get_disabled_credentials_mode(),
|
||||||
|
nym_api_endpoints: base_config.get_nym_api_endpoints(),
|
||||||
|
bandwidth_controller,
|
||||||
|
reply_storage_backend,
|
||||||
|
key_manager,
|
||||||
custom_topology_provider: None,
|
custom_topology_provider: None,
|
||||||
setup_method: GatewaySetup::MustLoad,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
|
pub fn new(
|
||||||
self.setup_method = setup;
|
gateway_config: &'a GatewayEndpointConfig,
|
||||||
self
|
debug_config: &'a DebugConfig,
|
||||||
|
key_manager: KeyManager,
|
||||||
|
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||||
|
reply_storage_backend: B,
|
||||||
|
credentials_toggle: CredentialsToggle,
|
||||||
|
nym_api_endpoints: Vec<Url>,
|
||||||
|
) -> BaseClientBuilder<'a, B, C, St> {
|
||||||
|
BaseClientBuilder {
|
||||||
|
gateway_config,
|
||||||
|
debug_config,
|
||||||
|
disabled_credentials: credentials_toggle.is_disabled(),
|
||||||
|
nym_api_endpoints,
|
||||||
|
reply_storage_backend,
|
||||||
|
custom_topology_provider: None,
|
||||||
|
bandwidth_controller,
|
||||||
|
key_manager,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_topology_provider(
|
pub fn with_topology_provider(mut self, provider: Box<dyn TopologyProvider>) -> Self {
|
||||||
mut self,
|
|
||||||
provider: Box<dyn TopologyProvider + Send + Sync>,
|
|
||||||
) -> Self {
|
|
||||||
self.custom_topology_provider = Some(provider);
|
self.custom_topology_provider = Some(provider);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: do **NOT** make this method public as its only valid usage is from within `start_base`
|
pub fn as_mix_recipient(&self) -> Recipient {
|
||||||
// because it relies on the crypto keys being already loaded
|
|
||||||
fn mix_address(
|
|
||||||
managed_keys: &ManagedKeys,
|
|
||||||
gateway_config: &GatewayEndpointConfig,
|
|
||||||
) -> Recipient {
|
|
||||||
Recipient::new(
|
Recipient::new(
|
||||||
*managed_keys.identity_public_key(),
|
*self.key_manager.identity_keypair().public_key(),
|
||||||
*managed_keys.encryption_public_key(),
|
*self.key_manager.encryption_keypair().public_key(),
|
||||||
// TODO: below only works under assumption that gateway address == gateway id
|
// TODO: below only works under assumption that gateway address == gateway id
|
||||||
// (which currently is true)
|
// (which currently is true)
|
||||||
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
|
NodeIdentity::from_base58_string(&self.gateway_config.gateway_id).unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +262,6 @@ where
|
|||||||
lane_queue_lengths: LaneQueueLengths,
|
lane_queue_lengths: LaneQueueLengths,
|
||||||
client_connection_rx: ConnectionCommandReceiver,
|
client_connection_rx: ConnectionCommandReceiver,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
packet_type: PacketType,
|
|
||||||
) {
|
) {
|
||||||
info!("Starting real traffic stream...");
|
info!("Starting real traffic stream...");
|
||||||
|
|
||||||
@@ -266,7 +277,7 @@ where
|
|||||||
lane_queue_lengths,
|
lane_queue_lengths,
|
||||||
client_connection_rx,
|
client_connection_rx,
|
||||||
)
|
)
|
||||||
.start_with_shutdown(shutdown, packet_type);
|
.start_with_shutdown(shutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
// buffer controlling all messages fetched from provider
|
// buffer controlling all messages fetched from provider
|
||||||
@@ -292,55 +303,60 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn start_gateway_client(
|
async fn start_gateway_client(
|
||||||
config: &Config,
|
&mut self,
|
||||||
gateway_config: GatewayEndpointConfig,
|
|
||||||
managed_keys: &ManagedKeys,
|
|
||||||
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
|
|
||||||
mixnet_message_sender: MixnetMessageSender,
|
mixnet_message_sender: MixnetMessageSender,
|
||||||
ack_sender: AcknowledgementSender,
|
ack_sender: AcknowledgementSender,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
|
) -> Result<GatewayClient<C, St>, ClientCoreError> {
|
||||||
where
|
let gateway_id = self.gateway_config.gateway_id.clone();
|
||||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
|
if gateway_id.is_empty() {
|
||||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
|
return Err(ClientCoreError::GatewayIdUnknown);
|
||||||
{
|
}
|
||||||
let gateway_address = gateway_config.gateway_listener.clone();
|
let gateway_address = self.gateway_config.gateway_listener.clone();
|
||||||
let gateway_id = gateway_config.gateway_id;
|
if gateway_address.is_empty() {
|
||||||
|
return Err(ClientCoreError::GatewayAddressUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: in theory, at this point, this should be infallible
|
|
||||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||||
|
|
||||||
|
// disgusting wasm workaround since there's no key persistence there (nor `client init`)
|
||||||
|
let shared_key = if self.key_manager.is_gateway_key_set() {
|
||||||
|
Some(self.key_manager.gateway_shared_key())
|
||||||
|
} else {
|
||||||
|
log::info!("Gateway key not set! Will proceed anyway.");
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut gateway_client = GatewayClient::new(
|
let mut gateway_client = GatewayClient::new(
|
||||||
gateway_address,
|
gateway_address,
|
||||||
managed_keys.identity_keypair(),
|
self.key_manager.identity_keypair(),
|
||||||
gateway_identity,
|
gateway_identity,
|
||||||
Some(managed_keys.must_get_gateway_shared_key()),
|
shared_key,
|
||||||
mixnet_message_sender,
|
mixnet_message_sender,
|
||||||
ack_sender,
|
ack_sender,
|
||||||
config.debug.gateway_connection.gateway_response_timeout,
|
self.debug_config
|
||||||
bandwidth_controller,
|
.gateway_connection
|
||||||
|
.gateway_response_timeout,
|
||||||
|
self.bandwidth_controller.take(),
|
||||||
shutdown,
|
shutdown,
|
||||||
);
|
);
|
||||||
|
|
||||||
gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
|
gateway_client.set_disabled_credentials_mode(self.disabled_credentials);
|
||||||
|
|
||||||
let shared_key = gateway_client
|
gateway_client
|
||||||
.authenticate_and_start()
|
.authenticate_and_start()
|
||||||
.await
|
.await
|
||||||
.tap_err(|err| {
|
.tap_err(|err| {
|
||||||
log::error!("Could not authenticate and start up the gateway connection - {err}")
|
log::error!("Could not authenticate and start up the gateway connection - {err}")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
managed_keys.ensure_gateway_key(shared_key);
|
|
||||||
|
|
||||||
Ok(gateway_client)
|
Ok(gateway_client)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_topology_provider(
|
fn setup_topology_provider(
|
||||||
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
custom_provider: Option<Box<dyn TopologyProvider>>,
|
||||||
nym_api_urls: Vec<Url>,
|
nym_api_urls: Vec<Url>,
|
||||||
) -> Box<dyn TopologyProvider + Send + Sync> {
|
) -> Box<dyn TopologyProvider> {
|
||||||
// if no custom provider was ... provided ..., create one using nym-api
|
// if no custom provider was ... provided ..., create one using nym-api
|
||||||
custom_provider.unwrap_or_else(|| {
|
custom_provider.unwrap_or_else(|| {
|
||||||
Box::new(NymApiTopologyProvider::new(
|
Box::new(NymApiTopologyProvider::new(
|
||||||
@@ -353,7 +369,7 @@ where
|
|||||||
// future responsible for periodically polling directory server and updating
|
// future responsible for periodically polling directory server and updating
|
||||||
// the current global view of topology
|
// the current global view of topology
|
||||||
async fn start_topology_refresher(
|
async fn start_topology_refresher(
|
||||||
topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
topology_provider: Box<dyn TopologyProvider>,
|
||||||
topology_config: config::Topology,
|
topology_config: config::Topology,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
mut shutdown: TaskClient,
|
mut shutdown: TaskClient,
|
||||||
@@ -398,82 +414,55 @@ where
|
|||||||
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
|
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
|
||||||
// requests?
|
// requests?
|
||||||
fn start_mix_traffic_controller(
|
fn start_mix_traffic_controller(
|
||||||
gateway_client: GatewayClient<C, S::CredentialStore>,
|
gateway_client: GatewayClient<C, St>,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
) -> BatchMixMessageSender
|
) -> BatchMixMessageSender {
|
||||||
where
|
|
||||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
info!("Starting mix traffic controller...");
|
info!("Starting mix traffic controller...");
|
||||||
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
|
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
|
||||||
mix_traffic_controller.start_with_shutdown(shutdown);
|
mix_traffic_controller.start_with_shutdown(shutdown);
|
||||||
mix_tx
|
mix_tx
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rename it as it implies the data is persistent whilst one can use InMemBackend
|
|
||||||
async fn setup_persistent_reply_storage(
|
async fn setup_persistent_reply_storage(
|
||||||
backend: S::ReplyStore,
|
backend: B,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
) -> Result<CombinedReplyStorage, ClientCoreError>
|
) -> Result<CombinedReplyStorage, ClientCoreError>
|
||||||
where
|
where
|
||||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
<B as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||||
S::ReplyStore: Send + Sync,
|
|
||||||
{
|
{
|
||||||
log::trace!("Setup persistent reply storage");
|
if backend.is_active() {
|
||||||
let persistent_storage = PersistentReplyStorage::new(backend);
|
log::trace!("Setup persistent reply storage");
|
||||||
let mem_store = persistent_storage
|
let persistent_storage = PersistentReplyStorage::new(backend);
|
||||||
.load_state_from_backend()
|
let mem_store = persistent_storage
|
||||||
.await
|
.load_state_from_backend()
|
||||||
.map_err(|err| ClientCoreError::SurbStorageError {
|
|
||||||
source: Box::new(err),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let store_clone = mem_store.clone();
|
|
||||||
spawn_future(async move {
|
|
||||||
persistent_storage
|
|
||||||
.flush_on_shutdown(store_clone, shutdown)
|
|
||||||
.await
|
.await
|
||||||
});
|
.map_err(|err| ClientCoreError::SurbStorageError {
|
||||||
|
source: Box::new(err),
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(mem_store)
|
let store_clone = mem_store.clone();
|
||||||
}
|
spawn_future(async move {
|
||||||
|
persistent_storage
|
||||||
|
.flush_on_shutdown(store_clone, shutdown)
|
||||||
|
.await
|
||||||
|
});
|
||||||
|
|
||||||
async fn initialise_keys_and_gateway(&self) -> Result<InitialisationDetails, ClientCoreError>
|
Ok(mem_store)
|
||||||
where
|
} else {
|
||||||
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
|
log::trace!("Setup inactive reply storage");
|
||||||
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
|
Ok(backend
|
||||||
{
|
.get_inactive_storage()
|
||||||
setup_gateway(
|
.map_err(|err| ClientCoreError::SurbStorageError {
|
||||||
&self.setup_method,
|
source: Box::new(err),
|
||||||
self.client_store.key_store(),
|
})?)
|
||||||
self.client_store.gateway_details_store(),
|
}
|
||||||
false,
|
|
||||||
Some(&self.config.client.nym_api_urls),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
|
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
|
||||||
where
|
where
|
||||||
S::ReplyStore: Send + Sync,
|
<B as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
|
||||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
|
||||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
|
|
||||||
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
|
|
||||||
{
|
{
|
||||||
info!("Starting nym client");
|
info!("Starting nym client");
|
||||||
|
|
||||||
// derive (or load) client keys and gateway configuration
|
|
||||||
let details = self.initialise_keys_and_gateway().await?;
|
|
||||||
let gateway_config = details.gateway_details;
|
|
||||||
let managed_keys = details.managed_keys;
|
|
||||||
|
|
||||||
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
|
|
||||||
|
|
||||||
let bandwidth_controller = self
|
|
||||||
.dkg_query_client
|
|
||||||
.map(|client| BandwidthController::new(credential_store, client));
|
|
||||||
|
|
||||||
// channels for inter-component communication
|
// channels for inter-component communication
|
||||||
// TODO: make the channels be internally created by the relevant components
|
// TODO: make the channels be internally created by the relevant components
|
||||||
// rather than creating them here, so say for example the buffer controller would create the request channels
|
// rather than creating them here, so say for example the buffer controller would create the request channels
|
||||||
@@ -500,39 +489,34 @@ where
|
|||||||
let (reply_controller_sender, reply_controller_receiver) =
|
let (reply_controller_sender, reply_controller_receiver) =
|
||||||
reply_controller::requests::new_control_channels();
|
reply_controller::requests::new_control_channels();
|
||||||
|
|
||||||
let self_address = Self::mix_address(&managed_keys, &gateway_config);
|
let self_address = self.as_mix_recipient();
|
||||||
|
|
||||||
// the components are started in very specific order. Unless you know what you are doing,
|
// the components are started in very specific order. Unless you know what you are doing,
|
||||||
// do not change that.
|
// do not change that.
|
||||||
let gateway_client = Self::start_gateway_client(
|
let gateway_client = self
|
||||||
self.config,
|
.start_gateway_client(mixnet_messages_sender, ack_sender, task_manager.subscribe())
|
||||||
gateway_config,
|
.await?;
|
||||||
&managed_keys,
|
|
||||||
bandwidth_controller,
|
let reply_storage = Self::setup_persistent_reply_storage(
|
||||||
mixnet_messages_sender,
|
self.reply_storage_backend,
|
||||||
ack_sender,
|
|
||||||
task_manager.subscribe(),
|
task_manager.subscribe(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let reply_storage =
|
|
||||||
Self::setup_persistent_reply_storage(reply_storage_backend, task_manager.subscribe())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let topology_provider = Self::setup_topology_provider(
|
let topology_provider = Self::setup_topology_provider(
|
||||||
self.custom_topology_provider.take(),
|
self.custom_topology_provider.take(),
|
||||||
self.config.get_nym_api_endpoints(),
|
self.nym_api_endpoints,
|
||||||
);
|
);
|
||||||
Self::start_topology_refresher(
|
Self::start_topology_refresher(
|
||||||
topology_provider,
|
topology_provider,
|
||||||
self.config.debug.topology,
|
self.debug_config.topology,
|
||||||
shared_topology_accessor.clone(),
|
shared_topology_accessor.clone(),
|
||||||
task_manager.subscribe(),
|
task_manager.subscribe(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Self::start_received_messages_buffer_controller(
|
Self::start_received_messages_buffer_controller(
|
||||||
managed_keys.encryption_keypair(),
|
self.key_manager.encryption_keypair(),
|
||||||
received_buffer_request_receiver,
|
received_buffer_request_receiver,
|
||||||
mixnet_messages_receiver,
|
mixnet_messages_receiver,
|
||||||
reply_storage.key_storage(),
|
reply_storage.key_storage(),
|
||||||
@@ -556,8 +540,8 @@ where
|
|||||||
let shared_lane_queue_lengths = LaneQueueLengths::new();
|
let shared_lane_queue_lengths = LaneQueueLengths::new();
|
||||||
|
|
||||||
let controller_config = real_messages_control::Config::new(
|
let controller_config = real_messages_control::Config::new(
|
||||||
&self.config.debug,
|
self.debug_config,
|
||||||
managed_keys.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
self_address,
|
self_address,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -573,18 +557,16 @@ where
|
|||||||
shared_lane_queue_lengths.clone(),
|
shared_lane_queue_lengths.clone(),
|
||||||
client_connection_rx,
|
client_connection_rx,
|
||||||
task_manager.subscribe(),
|
task_manager.subscribe(),
|
||||||
self.config.debug.traffic.packet_type,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self
|
if !self
|
||||||
.config
|
.debug_config
|
||||||
.debug
|
|
||||||
.cover_traffic
|
.cover_traffic
|
||||||
.disable_loop_cover_traffic_stream
|
.disable_loop_cover_traffic_stream
|
||||||
{
|
{
|
||||||
Self::start_cover_traffic_stream(
|
Self::start_cover_traffic_stream(
|
||||||
&self.config.debug,
|
self.debug_config,
|
||||||
managed_keys.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
self_address,
|
self_address,
|
||||||
shared_topology_accessor.clone(),
|
shared_topology_accessor.clone(),
|
||||||
message_sender,
|
message_sender,
|
||||||
@@ -596,7 +578,6 @@ where
|
|||||||
debug!("The address of this client is: {self_address}");
|
debug!("The address of this client is: {self_address}");
|
||||||
|
|
||||||
Ok(BaseClient {
|
Ok(BaseClient {
|
||||||
address: self_address,
|
|
||||||
client_input: ClientInputStatus::AwaitingProducer {
|
client_input: ClientInputStatus::AwaitingProducer {
|
||||||
client_input: ClientInput {
|
client_input: ClientInput {
|
||||||
connection_command_sender: client_connection_tx,
|
connection_command_sender: client_connection_tx,
|
||||||
@@ -619,7 +600,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct BaseClient {
|
pub struct BaseClient {
|
||||||
pub address: Recipient,
|
|
||||||
pub client_input: ClientInputStatus,
|
pub client_input: ClientInputStatus,
|
||||||
pub client_output: ClientOutputStatus,
|
pub client_output: ClientOutputStatus,
|
||||||
pub client_state: ClientState,
|
pub client_state: ClientState,
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::replies::reply_storage::{
|
use crate::client::replies::reply_storage::{
|
||||||
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||||
};
|
};
|
||||||
use crate::config;
|
use crate::config::DebugConfig;
|
||||||
use crate::config::Config;
|
|
||||||
use crate::error::ClientCoreError;
|
use crate::error::ClientCoreError;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use nym_bandwidth_controller::BandwidthController;
|
|
||||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
|
||||||
use nym_validator_client::nyxd::QueryNyxdClient;
|
|
||||||
use nym_validator_client::Client;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
async fn setup_fresh_backend<P: AsRef<Path>>(
|
async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||||
db_path: P,
|
db_path: P,
|
||||||
surb_config: &config::ReplySurbs,
|
debug_config: &DebugConfig,
|
||||||
) -> Result<fs_backend::Backend, ClientCoreError> {
|
) -> Result<fs_backend::Backend, ClientCoreError> {
|
||||||
info!("creating fresh surb database");
|
info!("creating fresh surb database");
|
||||||
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
|
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
|
||||||
@@ -36,8 +30,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
|||||||
// it will only be happening on the very first run and in practice won't incur huge
|
// it will only be happening on the very first run and in practice won't incur huge
|
||||||
// costs since the storage is going to be empty
|
// costs since the storage is going to be empty
|
||||||
let mem_store = CombinedReplyStorage::new(
|
let mem_store = CombinedReplyStorage::new(
|
||||||
surb_config.minimum_reply_surb_storage_threshold,
|
debug_config
|
||||||
surb_config.maximum_reply_surb_storage_threshold,
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
);
|
);
|
||||||
storage_backend
|
storage_backend
|
||||||
.init_fresh(&mem_store)
|
.init_fresh(&mem_store)
|
||||||
@@ -49,13 +47,17 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
|||||||
Ok(storage_backend)
|
Ok(storage_backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn setup_inactive_backend(surb_config: &config::ReplySurbs) -> fs_backend::Backend {
|
fn setup_inactive_backend(debug_config: &DebugConfig) -> fs_backend::Backend {
|
||||||
// info!("creating inactive surb database");
|
info!("creating inactive surb database");
|
||||||
// fs_backend::Backend::new_inactive(
|
fs_backend::Backend::new_inactive(
|
||||||
// surb_config.minimum_reply_surb_storage_threshold,
|
debug_config
|
||||||
// surb_config.maximum_reply_surb_storage_threshold,
|
.reply_surbs
|
||||||
// )
|
.minimum_reply_surb_storage_threshold,
|
||||||
// }
|
debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
||||||
let db_path = db_path.as_ref();
|
let db_path = db_path.as_ref();
|
||||||
@@ -79,73 +81,28 @@ fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||||
db_path: P,
|
db_path: Option<P>,
|
||||||
surb_config: &config::ReplySurbs,
|
debug_config: &DebugConfig,
|
||||||
) -> Result<fs_backend::Backend, ClientCoreError> {
|
) -> Result<fs_backend::Backend, ClientCoreError> {
|
||||||
// if the database file doesnt exist, initialise fresh storage, otherwise attempt to load
|
if let Some(db_path) = db_path {
|
||||||
// the existing one
|
// if the database file doesnt exist, initialise fresh storage, otherwise attempt to load
|
||||||
let db_path = db_path.as_ref();
|
// the existing one
|
||||||
if db_path.exists() {
|
let db_path = db_path.as_ref();
|
||||||
info!("loading existing surb database");
|
if db_path.exists() {
|
||||||
match fs_backend::Backend::try_load(db_path).await {
|
info!("loading existing surb database");
|
||||||
Ok(backend) => Ok(backend),
|
match fs_backend::Backend::try_load(db_path).await {
|
||||||
Err(err) => {
|
Ok(backend) => Ok(backend),
|
||||||
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
Err(err) => {
|
||||||
|
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||||
|
|
||||||
archive_corrupted_database(db_path)?;
|
archive_corrupted_database(db_path)?;
|
||||||
setup_fresh_backend(db_path, surb_config).await
|
setup_fresh_backend(db_path, debug_config).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setup_fresh_backend(db_path, debug_config).await
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setup_fresh_backend(db_path, surb_config).await
|
Ok(setup_inactive_backend(debug_config))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_bandwidth_controller<St: CredentialStorage>(
|
|
||||||
config: &Config,
|
|
||||||
storage: St,
|
|
||||||
) -> BandwidthController<Client<QueryNyxdClient>, St> {
|
|
||||||
let nyxd_url = config
|
|
||||||
.get_validator_endpoints()
|
|
||||||
.pop()
|
|
||||||
.expect("No nyxd validator endpoint provided");
|
|
||||||
let api_url = config
|
|
||||||
.get_nym_api_endpoints()
|
|
||||||
.pop()
|
|
||||||
.expect("No validator api endpoint provided");
|
|
||||||
|
|
||||||
create_bandwidth_controller_with_urls(nyxd_url, api_url, storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>(
|
|
||||||
nyxd_url: Url,
|
|
||||||
nym_api_url: Url,
|
|
||||||
storage: St,
|
|
||||||
) -> BandwidthController<Client<QueryNyxdClient>, St> {
|
|
||||||
let client = default_query_dkg_client(nyxd_url, nym_api_url);
|
|
||||||
|
|
||||||
BandwidthController::new(storage, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_query_dkg_client_from_config(config: &Config) -> Client<QueryNyxdClient> {
|
|
||||||
let nyxd_url = config
|
|
||||||
.get_validator_endpoints()
|
|
||||||
.pop()
|
|
||||||
.expect("No nyxd validator endpoint provided");
|
|
||||||
let api_url = config
|
|
||||||
.get_nym_api_endpoints()
|
|
||||||
.pop()
|
|
||||||
.expect("No validator api endpoint provided");
|
|
||||||
|
|
||||||
default_query_dkg_client(nyxd_url, api_url)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_query_dkg_client(nyxd_url: Url, nym_api_url: Url) -> Client<QueryNyxdClient> {
|
|
||||||
let details = nym_network_defaults::NymNetworkDetails::new_from_env();
|
|
||||||
let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details)
|
|
||||||
.expect("failed to construct validator client config");
|
|
||||||
// overwrite env configuration with config URLs
|
|
||||||
client_config = client_config.with_urls(nyxd_url, nym_api_url);
|
|
||||||
nym_validator_client::Client::new_query(client_config)
|
|
||||||
.expect("Could not construct query client")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::config::GatewayEndpointConfig;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
use std::error::Error;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use zeroize::Zeroizing;
|
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
|
||||||
pub trait GatewayDetailsStore {
|
|
||||||
type StorageError: Error;
|
|
||||||
|
|
||||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError>;
|
|
||||||
|
|
||||||
async fn store_gateway_details(
|
|
||||||
&self,
|
|
||||||
details: &PersistedGatewayDetails,
|
|
||||||
) -> Result<(), Self::StorageError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct PersistedGatewayDetails {
|
|
||||||
// TODO: should we also verify correctness of the details themselves?
|
|
||||||
// i.e. we could include a checksum or tag (via the shared keys)
|
|
||||||
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
|
|
||||||
// in order to actually use it, we'd have to recompute the whole checksum which would be a huge pain.
|
|
||||||
/// The hash of the shared keys to ensure the correct ones are used with those gateway details.
|
|
||||||
#[serde(with = "base64")]
|
|
||||||
key_hash: Vec<u8>,
|
|
||||||
|
|
||||||
/// Actual gateway details being persisted.
|
|
||||||
pub(crate) details: GatewayEndpointConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PersistedGatewayDetails> for GatewayEndpointConfig {
|
|
||||||
fn from(value: PersistedGatewayDetails) -> Self {
|
|
||||||
value.details
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PersistedGatewayDetails {
|
|
||||||
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
|
|
||||||
let key_bytes = Zeroizing::new(shared_key.to_bytes());
|
|
||||||
|
|
||||||
let mut key_hasher = Sha256::new();
|
|
||||||
key_hasher.update(&key_bytes);
|
|
||||||
let key_hash = key_hasher.finalize().to_vec();
|
|
||||||
|
|
||||||
PersistedGatewayDetails { key_hash, details }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
|
|
||||||
let key_bytes = Zeroizing::new(shared_key.to_bytes());
|
|
||||||
|
|
||||||
let mut key_hasher = Sha256::new();
|
|
||||||
key_hasher.update(&key_bytes);
|
|
||||||
let key_hash = key_hasher.finalize();
|
|
||||||
|
|
||||||
self.key_hash == key_hash.deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper to make Vec<u8> serialization use base64 representation to make it human readable
|
|
||||||
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
|
|
||||||
mod base64 {
|
|
||||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
|
||||||
use serde::{Deserialize, Deserializer, Serializer};
|
|
||||||
|
|
||||||
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.serialize_str(&STANDARD.encode(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
|
||||||
let s = <String>::deserialize(deserializer)?;
|
|
||||||
STANDARD.decode(s).map_err(serde::de::Error::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum OnDiskGatewayDetailsError {
|
|
||||||
#[error("JSON failure: {0}")]
|
|
||||||
SerializationFailure(#[from] serde_json::Error),
|
|
||||||
|
|
||||||
#[error("failed to store gateway details to {path}: {err}")]
|
|
||||||
StoreFailure {
|
|
||||||
path: String,
|
|
||||||
#[source]
|
|
||||||
err: std::io::Error,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("failed to load gateway details from {path}: {err}")]
|
|
||||||
LoadFailure {
|
|
||||||
path: String,
|
|
||||||
#[source]
|
|
||||||
err: std::io::Error,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
pub struct OnDiskGatewayDetails {
|
|
||||||
file_location: std::path::PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
impl OnDiskGatewayDetails {
|
|
||||||
pub fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
|
|
||||||
OnDiskGatewayDetails {
|
|
||||||
file_location: path.as_ref().to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
|
|
||||||
let file = std::fs::File::open(&self.file_location).map_err(|err| {
|
|
||||||
OnDiskGatewayDetailsError::LoadFailure {
|
|
||||||
path: self.file_location.display().to_string(),
|
|
||||||
err,
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(serde_json::from_reader(file)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn store_to_disk(
|
|
||||||
&self,
|
|
||||||
details: &PersistedGatewayDetails,
|
|
||||||
) -> Result<(), OnDiskGatewayDetailsError> {
|
|
||||||
// ensure the whole directory structure exists
|
|
||||||
if let Some(parent_dir) = &self.file_location.parent() {
|
|
||||||
std::fs::create_dir_all(parent_dir).map_err(|err| {
|
|
||||||
OnDiskGatewayDetailsError::StoreFailure {
|
|
||||||
path: self.file_location.display().to_string(),
|
|
||||||
err,
|
|
||||||
}
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = std::fs::File::create(&self.file_location).map_err(|err| {
|
|
||||||
OnDiskGatewayDetailsError::StoreFailure {
|
|
||||||
path: self.file_location.display().to_string(),
|
|
||||||
err,
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(serde_json::to_writer_pretty(file, details)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
|
||||||
impl GatewayDetailsStore for OnDiskGatewayDetails {
|
|
||||||
type StorageError = OnDiskGatewayDetailsError;
|
|
||||||
|
|
||||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
|
||||||
self.load_from_disk()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_gateway_details(
|
|
||||||
&self,
|
|
||||||
gateway_details: &PersistedGatewayDetails,
|
|
||||||
) -> Result<(), Self::StorageError> {
|
|
||||||
self.store_to_disk(gateway_details)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct InMemGatewayDetails {
|
|
||||||
details: Mutex<Option<PersistedGatewayDetails>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[error("old ephemeral gateway details can't be loaded from storage")]
|
|
||||||
pub struct EphemeralGatewayDetailsError;
|
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
|
||||||
impl GatewayDetailsStore for InMemGatewayDetails {
|
|
||||||
type StorageError = EphemeralGatewayDetailsError;
|
|
||||||
|
|
||||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
|
||||||
self.details
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.clone()
|
|
||||||
.ok_or(EphemeralGatewayDetailsError)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_gateway_details(
|
|
||||||
&self,
|
|
||||||
gateway_details: &PersistedGatewayDetails,
|
|
||||||
) -> Result<(), Self::StorageError> {
|
|
||||||
*self.details.lock().await = Some(gateway_details.clone());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// TODO: combine those more closely. Perhaps into a single underlying store.
|
|
||||||
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
|
|
||||||
|
|
||||||
use crate::client::base_client::storage::gateway_details::{
|
|
||||||
GatewayDetailsStore, InMemGatewayDetails,
|
|
||||||
};
|
|
||||||
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
|
|
||||||
use crate::client::replies::reply_storage;
|
|
||||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
|
||||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
|
||||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
|
||||||
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
use crate::client::base_client::non_wasm_helpers;
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
use crate::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
use crate::client::key_manager::persistence::OnDiskKeys;
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
use crate::client::replies::reply_storage::fs_backend;
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
use crate::config::{self, disk_persistence::CommonClientPaths};
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
use crate::error::ClientCoreError;
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
|
|
||||||
|
|
||||||
pub mod gateway_details;
|
|
||||||
|
|
||||||
// TODO: ideally this should be changed into
|
|
||||||
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewayDetailsStore`
|
|
||||||
pub trait MixnetClientStorage {
|
|
||||||
type KeyStore: KeyStore;
|
|
||||||
type ReplyStore: ReplyStorageBackend;
|
|
||||||
type CredentialStore: CredentialStorage;
|
|
||||||
type GatewayDetailsStore: GatewayDetailsStore;
|
|
||||||
|
|
||||||
// this is a TERRIBLE name...
|
|
||||||
// fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore, Self::GatewayDetailsStore);
|
|
||||||
|
|
||||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
|
|
||||||
|
|
||||||
fn key_store(&self) -> &Self::KeyStore;
|
|
||||||
fn reply_store(&self) -> &Self::ReplyStore;
|
|
||||||
fn credential_store(&self) -> &Self::CredentialStore;
|
|
||||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Ephemeral {
|
|
||||||
key_store: InMemEphemeralKeys,
|
|
||||||
reply_store: reply_storage::Empty,
|
|
||||||
credential_store: EphemeralCredentialStorage,
|
|
||||||
gateway_details_store: InMemGatewayDetails,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ephemeral {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MixnetClientStorage for Ephemeral {
|
|
||||||
type KeyStore = InMemEphemeralKeys;
|
|
||||||
type ReplyStore = reply_storage::Empty;
|
|
||||||
type CredentialStore = EphemeralCredentialStorage;
|
|
||||||
type GatewayDetailsStore = InMemGatewayDetails;
|
|
||||||
|
|
||||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
|
||||||
(self.reply_store, self.credential_store)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_store(&self) -> &Self::KeyStore {
|
|
||||||
&self.key_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reply_store(&self) -> &Self::ReplyStore {
|
|
||||||
&self.reply_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn credential_store(&self) -> &Self::CredentialStore {
|
|
||||||
&self.credential_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
|
||||||
&self.gateway_details_store
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
pub struct OnDiskPersistent {
|
|
||||||
pub(crate) key_store: OnDiskKeys,
|
|
||||||
pub(crate) reply_store: fs_backend::Backend,
|
|
||||||
pub(crate) credential_store: PersistentCredentialStorage,
|
|
||||||
pub(crate) gateway_details_store: OnDiskGatewayDetails,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
impl OnDiskPersistent {
|
|
||||||
pub fn new(
|
|
||||||
key_store: OnDiskKeys,
|
|
||||||
reply_store: fs_backend::Backend,
|
|
||||||
credential_store: PersistentCredentialStorage,
|
|
||||||
gateway_details_store: OnDiskGatewayDetails,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
key_store,
|
|
||||||
reply_store,
|
|
||||||
credential_store,
|
|
||||||
gateway_details_store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn from_paths(
|
|
||||||
paths: CommonClientPaths,
|
|
||||||
debug_config: &config::DebugConfig,
|
|
||||||
) -> Result<Self, ClientCoreError> {
|
|
||||||
let key_store = OnDiskKeys::new(paths.keys);
|
|
||||||
|
|
||||||
let reply_store = non_wasm_helpers::setup_fs_reply_surb_backend(
|
|
||||||
paths.reply_surb_database,
|
|
||||||
&debug_config.reply_surbs,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let credential_store =
|
|
||||||
nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await;
|
|
||||||
|
|
||||||
let gateway_details_store = OnDiskGatewayDetails::new(paths.gateway_details);
|
|
||||||
|
|
||||||
Ok(OnDiskPersistent {
|
|
||||||
key_store,
|
|
||||||
reply_store,
|
|
||||||
credential_store,
|
|
||||||
gateway_details_store,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
|
||||||
impl MixnetClientStorage for OnDiskPersistent {
|
|
||||||
type KeyStore = OnDiskKeys;
|
|
||||||
type ReplyStore = fs_backend::Backend;
|
|
||||||
type CredentialStore = PersistentCredentialStorage;
|
|
||||||
type GatewayDetailsStore = OnDiskGatewayDetails;
|
|
||||||
|
|
||||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
|
||||||
(self.reply_store, self.credential_store)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_store(&self) -> &Self::KeyStore {
|
|
||||||
&self.key_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reply_store(&self) -> &Self::ReplyStore {
|
|
||||||
&self.reply_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn credential_store(&self) -> &Self::CredentialStore {
|
|
||||||
&self.credential_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
|
||||||
&self.gateway_details_store
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -194,7 +194,6 @@ impl LoopCoverTrafficStream<OsRng> {
|
|||||||
self.average_ack_delay,
|
self.average_ack_delay,
|
||||||
self.cover_traffic.loop_cover_traffic_average_delay,
|
self.cover_traffic.loop_cover_traffic_average_delay,
|
||||||
cover_traffic_packet_size,
|
cover_traffic_packet_size,
|
||||||
nym_sphinx::params::PacketType::Mix,
|
|
||||||
)
|
)
|
||||||
.expect("Somehow failed to generate a loop cover message with a valid topology");
|
.expect("Somehow failed to generate a loop cover message with a valid topology");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
|
use log::*;
|
||||||
|
use nym_crypto::asymmetric::{encryption, identity};
|
||||||
|
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||||
|
use nym_sphinx::acknowledgements::AckKey;
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use std::io;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
// Note: to support key rotation in the future, all keys will require adding an extra smart pointer,
|
||||||
|
// most likely an AtomicCell, or if it doesn't work as I think it does, a Mutex. Although I think
|
||||||
|
// AtomicCell includes a Mutex implicitly if the underlying type does not work atomically.
|
||||||
|
// And I guess there will need to be some mechanism for a grace period when you can still
|
||||||
|
// use the old key after new one was issued.
|
||||||
|
|
||||||
|
// Remember that Arc<T> has Deref implementation for T
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct KeyManager {
|
||||||
|
/// identity key associated with the client instance.
|
||||||
|
identity_keypair: Arc<identity::KeyPair>,
|
||||||
|
|
||||||
|
/// encryption key associated with the client instance.
|
||||||
|
encryption_keypair: Arc<encryption::KeyPair>,
|
||||||
|
|
||||||
|
/// shared key derived with the gateway during "registration handshake"
|
||||||
|
gateway_shared_key: Option<Arc<SharedKeys>>,
|
||||||
|
|
||||||
|
/// key used for producing and processing acknowledgement packets.
|
||||||
|
ack_key: Arc<AckKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The expected flow of a KeyManager "lifetime" is as follows:
|
||||||
|
/*
|
||||||
|
1. ::new() is called during client-init
|
||||||
|
2. after gateway registration is completed [in init] ::insert_gateway_shared_key() is called
|
||||||
|
3. ::store_keys() is called before init finishes execution.
|
||||||
|
4. ::load_keys() is called at the beginning of each subsequent client-run
|
||||||
|
5. [not implemented] ::rotate_keys() is called periodically during client-run I presume?
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl KeyManager {
|
||||||
|
/// Creates new instance of a [`KeyManager`]
|
||||||
|
pub fn new<R>(rng: &mut R) -> Self
|
||||||
|
where
|
||||||
|
R: RngCore + CryptoRng,
|
||||||
|
{
|
||||||
|
KeyManager {
|
||||||
|
identity_keypair: Arc::new(identity::KeyPair::new(rng)),
|
||||||
|
encryption_keypair: Arc::new(encryption::KeyPair::new(rng)),
|
||||||
|
gateway_shared_key: None,
|
||||||
|
ack_key: Arc::new(AckKey::new(rng)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_keys(
|
||||||
|
id_keypair: identity::KeyPair,
|
||||||
|
enc_keypair: encryption::KeyPair,
|
||||||
|
gateway_shared_key: SharedKeys,
|
||||||
|
ack_key: AckKey,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
identity_keypair: Arc::new(id_keypair),
|
||||||
|
encryption_keypair: Arc::new(enc_keypair),
|
||||||
|
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
|
||||||
|
ack_key: Arc::new(ack_key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads previously stored client keys from the disk.
|
||||||
|
fn load_client_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||||
|
let identity_keypair: identity::KeyPair =
|
||||||
|
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||||
|
client_pathfinder.private_identity_key().to_owned(),
|
||||||
|
client_pathfinder.public_identity_key().to_owned(),
|
||||||
|
))?;
|
||||||
|
let encryption_keypair: encryption::KeyPair =
|
||||||
|
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||||
|
client_pathfinder.private_encryption_key().to_owned(),
|
||||||
|
client_pathfinder.public_encryption_key().to_owned(),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let ack_key: AckKey = nym_pemstore::load_key(client_pathfinder.ack_key())?;
|
||||||
|
|
||||||
|
Ok(KeyManager {
|
||||||
|
identity_keypair: Arc::new(identity_keypair),
|
||||||
|
encryption_keypair: Arc::new(encryption_keypair),
|
||||||
|
gateway_shared_key: None,
|
||||||
|
ack_key: Arc::new(ack_key),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads previously stored keys from the disk. Fails if not all, including the shared gateway
|
||||||
|
/// key, is available.
|
||||||
|
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||||
|
let mut key_manager = Self::load_client_keys(client_pathfinder)?;
|
||||||
|
|
||||||
|
let gateway_shared_key: SharedKeys =
|
||||||
|
nym_pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||||
|
|
||||||
|
key_manager.gateway_shared_key = Some(Arc::new(gateway_shared_key));
|
||||||
|
|
||||||
|
Ok(key_manager)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads previously stored keys from the disk. Fails if client keys are not availabe, but the
|
||||||
|
/// shared gateway key is optional.
|
||||||
|
pub fn load_keys_but_gateway_is_optional(
|
||||||
|
client_pathfinder: &ClientKeyPathfinder,
|
||||||
|
) -> io::Result<Self> {
|
||||||
|
let mut key_manager = Self::load_client_keys(client_pathfinder)?;
|
||||||
|
|
||||||
|
let gateway_shared_key: Result<SharedKeys, io::Error> =
|
||||||
|
nym_pemstore::load_key(client_pathfinder.gateway_shared_key());
|
||||||
|
|
||||||
|
// It's ok if the gateway key was not found
|
||||||
|
let gateway_shared_key = match gateway_shared_key {
|
||||||
|
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
Ok(key) => Ok(Some(key)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
key_manager.gateway_shared_key = gateway_shared_key.map(Arc::new);
|
||||||
|
|
||||||
|
Ok(key_manager)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores all available keys on the disk.
|
||||||
|
// While perhaps there is no much point in storing the `AckKey` on the disk,
|
||||||
|
// it is done so for the consistency sake so that you wouldn't require an rng instance
|
||||||
|
// during `load_keys` to generate the said key.
|
||||||
|
pub fn store_keys(&self, client_pathfinder: &ClientKeyPathfinder) -> io::Result<()> {
|
||||||
|
nym_pemstore::store_keypair(
|
||||||
|
self.identity_keypair.as_ref(),
|
||||||
|
&nym_pemstore::KeyPairPath::new(
|
||||||
|
client_pathfinder.private_identity_key().to_owned(),
|
||||||
|
client_pathfinder.public_identity_key().to_owned(),
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
nym_pemstore::store_keypair(
|
||||||
|
self.encryption_keypair.as_ref(),
|
||||||
|
&nym_pemstore::KeyPairPath::new(
|
||||||
|
client_pathfinder.private_encryption_key().to_owned(),
|
||||||
|
client_pathfinder.public_encryption_key().to_owned(),
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
nym_pemstore::store_key(self.ack_key.as_ref(), client_pathfinder.ack_key())?;
|
||||||
|
|
||||||
|
match self.gateway_shared_key.as_ref() {
|
||||||
|
None => debug!("No gateway shared key available to store!"),
|
||||||
|
Some(gate_key) => {
|
||||||
|
nym_pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_gateway_key(&self, client_pathfinder: &ClientKeyPathfinder) -> io::Result<()> {
|
||||||
|
match self.gateway_shared_key.as_ref() {
|
||||||
|
None => {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"trying to store a non-existing key",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Some(gate_key) => {
|
||||||
|
nym_pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overwrite the existing identity keypair
|
||||||
|
pub fn set_identity_keypair(&mut self, id_keypair: identity::KeyPair) {
|
||||||
|
self.identity_keypair = Arc::new(id_keypair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets an atomically reference counted pointer to [`identity::KeyPair`].
|
||||||
|
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
|
||||||
|
Arc::clone(&self.identity_keypair)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overwrite the existing encryption keypair
|
||||||
|
pub fn set_encryption_keypair(&mut self, enc_keypair: encryption::KeyPair) {
|
||||||
|
self.encryption_keypair = Arc::new(enc_keypair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets an atomically reference counted pointer to [`encryption::KeyPair`].
|
||||||
|
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
|
||||||
|
Arc::clone(&self.encryption_keypair)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overwrite the existing ack key
|
||||||
|
pub fn set_ack_key(&mut self, ack_key: AckKey) {
|
||||||
|
self.ack_key = Arc::new(ack_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets an atomically reference counted pointer to [`AckKey`].
|
||||||
|
pub fn ack_key(&self) -> Arc<AckKey> {
|
||||||
|
Arc::clone(&self.ack_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`].
|
||||||
|
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: Arc<SharedKeys>) {
|
||||||
|
self.gateway_shared_key = Some(gateway_shared_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets an atomically reference counted pointer to [`SharedKey`].
|
||||||
|
// since this function is not fully public, it is not expected to be used externally and
|
||||||
|
// hence it's up to us to ensure it's called in correct context
|
||||||
|
pub fn gateway_shared_key(&self) -> Arc<SharedKeys> {
|
||||||
|
Arc::clone(
|
||||||
|
self.gateway_shared_key
|
||||||
|
.as_ref()
|
||||||
|
.expect("tried to unwrap empty gateway key!"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_gateway_key_set(&self) -> bool {
|
||||||
|
self.gateway_shared_key.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::client::key_manager::persistence::KeyStore;
|
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
|
||||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
|
||||||
use nym_sphinx::acknowledgements::AckKey;
|
|
||||||
use rand::{CryptoRng, RngCore};
|
|
||||||
use std::fmt::{Debug, Formatter};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use zeroize::ZeroizeOnDrop;
|
|
||||||
|
|
||||||
pub mod persistence;
|
|
||||||
|
|
||||||
pub enum ManagedKeys {
|
|
||||||
Initial(KeyManagerBuilder),
|
|
||||||
FullyDerived(KeyManager),
|
|
||||||
|
|
||||||
// I really hate the existence of this variant, but I couldn't come up with a better way to handle
|
|
||||||
// `Self::deal_with_gateway_key` otherwise.
|
|
||||||
Invalidated,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ManagedKeys {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ManagedKeys::Initial(_) => write!(f, "initial"),
|
|
||||||
ManagedKeys::FullyDerived(_) => write!(f, "fully derived"),
|
|
||||||
ManagedKeys::Invalidated => write!(f, "invalidated"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<KeyManagerBuilder> for ManagedKeys {
|
|
||||||
fn from(value: KeyManagerBuilder) -> Self {
|
|
||||||
ManagedKeys::Initial(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<KeyManager> for ManagedKeys {
|
|
||||||
fn from(value: KeyManager) -> Self {
|
|
||||||
ManagedKeys::FullyDerived(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ManagedKeys {
|
|
||||||
pub fn is_valid(&self) -> bool {
|
|
||||||
!matches!(self, ManagedKeys::Invalidated)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn try_load<S: KeyStore>(key_store: &S) -> Result<Self, S::StorageError> {
|
|
||||||
Ok(ManagedKeys::FullyDerived(
|
|
||||||
KeyManager::load_keys(key_store).await?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_new<R>(rng: &mut R) -> Self
|
|
||||||
where
|
|
||||||
R: RngCore + CryptoRng,
|
|
||||||
{
|
|
||||||
ManagedKeys::Initial(KeyManagerBuilder::new(rng))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_or_generate<R, S>(rng: &mut R, key_store: &S) -> Self
|
|
||||||
where
|
|
||||||
R: RngCore + CryptoRng,
|
|
||||||
S: KeyStore,
|
|
||||||
{
|
|
||||||
Self::try_load(key_store)
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|_| Self::generate_new(rng))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
|
|
||||||
match self {
|
|
||||||
ManagedKeys::Initial(keys) => keys.identity_keypair(),
|
|
||||||
ManagedKeys::FullyDerived(keys) => keys.identity_keypair(),
|
|
||||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
|
|
||||||
match self {
|
|
||||||
ManagedKeys::Initial(keys) => keys.encryption_keypair(),
|
|
||||||
ManagedKeys::FullyDerived(keys) => keys.encryption_keypair(),
|
|
||||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ack_key(&self) -> Arc<AckKey> {
|
|
||||||
match self {
|
|
||||||
ManagedKeys::Initial(keys) => keys.ack_key(),
|
|
||||||
ManagedKeys::FullyDerived(keys) => keys.ack_key(),
|
|
||||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn must_get_gateway_shared_key(&self) -> Arc<SharedKeys> {
|
|
||||||
self.gateway_shared_key()
|
|
||||||
.expect("failed to extract gateway shared key")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
|
|
||||||
match self {
|
|
||||||
ManagedKeys::Initial(_) => None,
|
|
||||||
ManagedKeys::FullyDerived(keys) => Some(keys.gateway_shared_key()),
|
|
||||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn identity_public_key(&self) -> &identity::PublicKey {
|
|
||||||
match self {
|
|
||||||
ManagedKeys::Initial(keys) => keys.identity_keypair.public_key(),
|
|
||||||
ManagedKeys::FullyDerived(keys) => keys.identity_keypair.public_key(),
|
|
||||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encryption_public_key(&self) -> &encryption::PublicKey {
|
|
||||||
match self {
|
|
||||||
ManagedKeys::Initial(keys) => keys.encryption_keypair.public_key(),
|
|
||||||
ManagedKeys::FullyDerived(keys) => keys.encryption_keypair.public_key(),
|
|
||||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ensure_gateway_key(&self, gateway_shared_key: Arc<SharedKeys>) {
|
|
||||||
if let ManagedKeys::FullyDerived(key_manager) = &self {
|
|
||||||
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|
|
||||||
|| key_manager.gateway_shared_key != gateway_shared_key
|
|
||||||
{
|
|
||||||
// this should NEVER happen thus panic here
|
|
||||||
panic!("derived fresh gateway shared key whilst already holding one!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn deal_with_gateway_key<S: KeyStore>(
|
|
||||||
&mut self,
|
|
||||||
gateway_shared_key: Arc<SharedKeys>,
|
|
||||||
key_store: &S,
|
|
||||||
) -> Result<(), S::StorageError> {
|
|
||||||
let key_manager = match std::mem::replace(self, ManagedKeys::Invalidated) {
|
|
||||||
ManagedKeys::Initial(keys) => {
|
|
||||||
let key_manager = keys.insert_gateway_shared_key(gateway_shared_key);
|
|
||||||
key_manager.persist_keys(key_store).await?;
|
|
||||||
key_manager
|
|
||||||
}
|
|
||||||
ManagedKeys::FullyDerived(key_manager) => {
|
|
||||||
self.ensure_gateway_key(gateway_shared_key);
|
|
||||||
key_manager
|
|
||||||
}
|
|
||||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
|
||||||
};
|
|
||||||
|
|
||||||
*self = ManagedKeys::FullyDerived(key_manager);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// all of the keys really shouldn't be wrapped in `Arc`, but due to how the gateway client is currently
|
|
||||||
// constructed, changing that would require more work than what it's worth
|
|
||||||
pub struct KeyManagerBuilder {
|
|
||||||
/// identity key associated with the client instance.
|
|
||||||
identity_keypair: Arc<identity::KeyPair>,
|
|
||||||
|
|
||||||
/// encryption key associated with the client instance.
|
|
||||||
encryption_keypair: Arc<encryption::KeyPair>,
|
|
||||||
|
|
||||||
/// key used for producing and processing acknowledgement packets.
|
|
||||||
ack_key: Arc<AckKey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyManagerBuilder {
|
|
||||||
/// Creates new instance of a [`KeyManager`]
|
|
||||||
pub fn new<R>(rng: &mut R) -> Self
|
|
||||||
where
|
|
||||||
R: RngCore + CryptoRng,
|
|
||||||
{
|
|
||||||
KeyManagerBuilder {
|
|
||||||
identity_keypair: Arc::new(identity::KeyPair::new(rng)),
|
|
||||||
encryption_keypair: Arc::new(encryption::KeyPair::new(rng)),
|
|
||||||
ack_key: Arc::new(AckKey::new(rng)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_gateway_shared_key(self, gateway_shared_key: Arc<SharedKeys>) -> KeyManager {
|
|
||||||
KeyManager {
|
|
||||||
identity_keypair: self.identity_keypair,
|
|
||||||
encryption_keypair: self.encryption_keypair,
|
|
||||||
gateway_shared_key,
|
|
||||||
ack_key: self.ack_key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
|
|
||||||
Arc::clone(&self.identity_keypair)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
|
|
||||||
Arc::clone(&self.encryption_keypair)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ack_key(&self) -> Arc<AckKey> {
|
|
||||||
Arc::clone(&self.ack_key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: to support key rotation in the future, all keys will require adding an extra smart pointer,
|
|
||||||
// most likely an AtomicCell, or if it doesn't work as I think it does, a Mutex. Although I think
|
|
||||||
// AtomicCell includes a Mutex implicitly if the underlying type does not work atomically.
|
|
||||||
// And I guess there will need to be some mechanism for a grace period when you can still
|
|
||||||
// use the old key after new one was issued.
|
|
||||||
|
|
||||||
// Remember that Arc<T> has Deref implementation for T
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct KeyManager {
|
|
||||||
/// identity key associated with the client instance.
|
|
||||||
identity_keypair: Arc<identity::KeyPair>,
|
|
||||||
|
|
||||||
/// encryption key associated with the client instance.
|
|
||||||
encryption_keypair: Arc<encryption::KeyPair>,
|
|
||||||
|
|
||||||
/// shared key derived with the gateway during "registration handshake"
|
|
||||||
gateway_shared_key: Arc<SharedKeys>,
|
|
||||||
|
|
||||||
/// key used for producing and processing acknowledgement packets.
|
|
||||||
ack_key: Arc<AckKey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyManager {
|
|
||||||
pub fn from_keys(
|
|
||||||
id_keypair: identity::KeyPair,
|
|
||||||
enc_keypair: encryption::KeyPair,
|
|
||||||
gateway_shared_key: SharedKeys,
|
|
||||||
ack_key: AckKey,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
identity_keypair: Arc::new(id_keypair),
|
|
||||||
encryption_keypair: Arc::new(enc_keypair),
|
|
||||||
gateway_shared_key: Arc::new(gateway_shared_key),
|
|
||||||
ack_key: Arc::new(ack_key),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_keys<S: KeyStore>(store: &S) -> Result<Self, S::StorageError> {
|
|
||||||
store.load_keys().await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn persist_keys<S: KeyStore>(&self, store: &S) -> Result<(), S::StorageError> {
|
|
||||||
store.store_keys(self).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets an atomically reference counted pointer to [`identity::KeyPair`].
|
|
||||||
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
|
|
||||||
Arc::clone(&self.identity_keypair)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets an atomically reference counted pointer to [`encryption::KeyPair`].
|
|
||||||
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
|
|
||||||
Arc::clone(&self.encryption_keypair)
|
|
||||||
}
|
|
||||||
/// Gets an atomically reference counted pointer to [`AckKey`].
|
|
||||||
pub fn ack_key(&self) -> Arc<AckKey> {
|
|
||||||
Arc::clone(&self.ack_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets an atomically reference counted pointer to [`SharedKey`].
|
|
||||||
pub fn gateway_shared_key(&self) -> Arc<SharedKeys> {
|
|
||||||
Arc::clone(&self.gateway_shared_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_gateway_key(self) -> KeyManagerBuilder {
|
|
||||||
if Arc::strong_count(&self.gateway_shared_key) > 1 {
|
|
||||||
panic!("attempted to remove gateway key whilst still holding multiple references!")
|
|
||||||
}
|
|
||||||
KeyManagerBuilder {
|
|
||||||
identity_keypair: self.identity_keypair,
|
|
||||||
encryption_keypair: self.encryption_keypair,
|
|
||||||
ack_key: self.ack_key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _assert_keys_zeroize_on_drop() {
|
|
||||||
fn _assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
|
|
||||||
|
|
||||||
_assert_zeroize_on_drop::<identity::KeyPair>();
|
|
||||||
_assert_zeroize_on_drop::<encryption::KeyPair>();
|
|
||||||
_assert_zeroize_on_drop::<AckKey>();
|
|
||||||
_assert_zeroize_on_drop::<SharedKeys>();
|
|
||||||
}
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::client::key_manager::KeyManager;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::error::Error;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use nym_pemstore::KeyPairPath;
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use nym_sphinx::acknowledgements::AckKey;
|
|
||||||
|
|
||||||
// we have to define it as an async trait since wasm storage is async
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
|
||||||
pub trait KeyStore {
|
|
||||||
type StorageError: Error;
|
|
||||||
|
|
||||||
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError>;
|
|
||||||
|
|
||||||
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum OnDiskKeysError {
|
|
||||||
#[error("failed to load {keys} keys from {:?} (private key) and {:?} (public key): {err}", .paths.private_key_path, .paths.public_key_path)]
|
|
||||||
KeyPairLoadFailure {
|
|
||||||
keys: String,
|
|
||||||
paths: nym_pemstore::KeyPairPath,
|
|
||||||
#[source]
|
|
||||||
err: std::io::Error,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("failed to store {keys} keys to {:?} (private key) and {:?} (public key): {err}", .paths.private_key_path, .paths.public_key_path)]
|
|
||||||
KeyPairStoreFailure {
|
|
||||||
keys: String,
|
|
||||||
paths: nym_pemstore::KeyPairPath,
|
|
||||||
#[source]
|
|
||||||
err: std::io::Error,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("failed to load {key} key from {path}: {err}")]
|
|
||||||
KeyLoadFailure {
|
|
||||||
key: String,
|
|
||||||
path: String,
|
|
||||||
#[source]
|
|
||||||
err: std::io::Error,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("failed to store {key} key to {path}: {err}")]
|
|
||||||
KeyStoreFailure {
|
|
||||||
key: String,
|
|
||||||
path: String,
|
|
||||||
#[source]
|
|
||||||
err: std::io::Error,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
pub struct OnDiskKeys {
|
|
||||||
paths: ClientKeysPaths,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
impl From<ClientKeysPaths> for OnDiskKeys {
|
|
||||||
fn from(paths: ClientKeysPaths) -> Self {
|
|
||||||
OnDiskKeys { paths }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
impl OnDiskKeys {
|
|
||||||
pub fn new(paths: ClientKeysPaths) -> Self {
|
|
||||||
OnDiskKeys { paths }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn ephemeral_load_gateway_keys(
|
|
||||||
&self,
|
|
||||||
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
|
|
||||||
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
|
|
||||||
.map(zeroize::Zeroizing::new)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
|
|
||||||
let encryption_paths = self.paths.encryption_key_pair_path();
|
|
||||||
self.load_keypair(encryption_paths, "encryption keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn load_identity_keypair(&self) -> Result<identity::KeyPair, OnDiskKeysError> {
|
|
||||||
let identity_paths = self.paths.identity_key_pair_path();
|
|
||||||
self.load_keypair(identity_paths, "identity keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_key<T: PemStorableKey>(
|
|
||||||
&self,
|
|
||||||
path: &std::path::Path,
|
|
||||||
name: impl Into<String>,
|
|
||||||
) -> Result<T, OnDiskKeysError> {
|
|
||||||
nym_pemstore::load_key(path).map_err(|err| OnDiskKeysError::KeyLoadFailure {
|
|
||||||
key: name.into(),
|
|
||||||
path: path.to_str().map(|s| s.to_owned()).unwrap_or_default(),
|
|
||||||
err,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_keypair<T: PemStorableKeyPair>(
|
|
||||||
&self,
|
|
||||||
paths: KeyPairPath,
|
|
||||||
name: impl Into<String>,
|
|
||||||
) -> Result<T, OnDiskKeysError> {
|
|
||||||
nym_pemstore::load_keypair(&paths).map_err(|err| OnDiskKeysError::KeyPairLoadFailure {
|
|
||||||
keys: name.into(),
|
|
||||||
paths,
|
|
||||||
err,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_key<T: PemStorableKey>(
|
|
||||||
&self,
|
|
||||||
key: &T,
|
|
||||||
path: &std::path::Path,
|
|
||||||
name: impl Into<String>,
|
|
||||||
) -> Result<(), OnDiskKeysError> {
|
|
||||||
nym_pemstore::store_key(key, path).map_err(|err| OnDiskKeysError::KeyStoreFailure {
|
|
||||||
key: name.into(),
|
|
||||||
path: path.to_str().map(|s| s.to_owned()).unwrap_or_default(),
|
|
||||||
err,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_keypair<T: PemStorableKeyPair>(
|
|
||||||
&self,
|
|
||||||
keys: &T,
|
|
||||||
paths: KeyPairPath,
|
|
||||||
name: impl Into<String>,
|
|
||||||
) -> Result<(), OnDiskKeysError> {
|
|
||||||
nym_pemstore::store_keypair(keys, &paths).map_err(|err| {
|
|
||||||
OnDiskKeysError::KeyPairStoreFailure {
|
|
||||||
keys: name.into(),
|
|
||||||
paths,
|
|
||||||
err,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> {
|
|
||||||
let identity_keypair = self.load_identity_keypair()?;
|
|
||||||
let encryption_keypair = self.load_encryption_keypair()?;
|
|
||||||
|
|
||||||
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
|
|
||||||
let gateway_shared_key: SharedKeys =
|
|
||||||
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")?;
|
|
||||||
|
|
||||||
Ok(KeyManager::from_keys(
|
|
||||||
identity_keypair,
|
|
||||||
encryption_keypair,
|
|
||||||
gateway_shared_key,
|
|
||||||
ack_key,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> {
|
|
||||||
let identity_paths = self.paths.identity_key_pair_path();
|
|
||||||
let encryption_paths = self.paths.encryption_key_pair_path();
|
|
||||||
|
|
||||||
self.store_keypair(
|
|
||||||
keys.identity_keypair.as_ref(),
|
|
||||||
identity_paths,
|
|
||||||
"identity keys",
|
|
||||||
)?;
|
|
||||||
self.store_keypair(
|
|
||||||
keys.encryption_keypair.as_ref(),
|
|
||||||
encryption_paths,
|
|
||||||
"encryption keys",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?;
|
|
||||||
self.store_key(
|
|
||||||
keys.gateway_shared_key.as_ref(),
|
|
||||||
self.paths.gateway_shared_key(),
|
|
||||||
"gateway shared keys",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
|
||||||
impl KeyStore for OnDiskKeys {
|
|
||||||
type StorageError = OnDiskKeysError;
|
|
||||||
|
|
||||||
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
|
|
||||||
self.load_keys()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
|
|
||||||
self.store_keys(keys)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct InMemEphemeralKeys {
|
|
||||||
keys: Mutex<Option<KeyManager>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[error("old ephemeral keys can't be loaded from storage")]
|
|
||||||
pub struct EphemeralKeysError;
|
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
|
||||||
impl KeyStore for InMemEphemeralKeys {
|
|
||||||
type StorageError = EphemeralKeysError;
|
|
||||||
|
|
||||||
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
|
|
||||||
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
|
|
||||||
*self.keys.lock().await = Some(keys.clone());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,6 @@ impl<C, St> MixTrafficController<C, St>
|
|||||||
where
|
where
|
||||||
C: DkgQueryClient + Sync + Send + 'static,
|
C: DkgQueryClient + Sync + Send + 'static,
|
||||||
St: Storage + 'static,
|
St: Storage + 'static,
|
||||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
gateway_client: GatewayClient<C, St>,
|
gateway_client: GatewayClient<C, St>,
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ impl AcknowledgementListener {
|
|||||||
while !shutdown.is_shutdown() {
|
while !shutdown.is_shutdown() {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
acks = self.ack_receiver.next() => match acks {
|
acks = self.ack_receiver.next() => match acks {
|
||||||
Some(acks) => {self.handle_ack_receiver_item(acks).await}
|
Some(acks) => self.handle_ack_receiver_item(acks).await,
|
||||||
None => {
|
None => {
|
||||||
log::trace!("AcknowledgementListener: Stopping since channel closed");
|
log::trace!("AcknowledgementListener: Stopping since channel closed");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use futures::channel::mpsc;
|
|||||||
use log::*;
|
use log::*;
|
||||||
use nym_gateway_client::AcknowledgementReceiver;
|
use nym_gateway_client::AcknowledgementReceiver;
|
||||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||||
use nym_sphinx::params::{PacketSize, PacketType};
|
use nym_sphinx::params::PacketSize;
|
||||||
use nym_sphinx::{
|
use nym_sphinx::{
|
||||||
acknowledgements::AckKey,
|
acknowledgements::AckKey,
|
||||||
addressing::clients::Recipient,
|
addressing::clients::Recipient,
|
||||||
@@ -249,11 +249,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn start_with_shutdown(
|
pub(super) fn start_with_shutdown(self, shutdown: nym_task::TaskClient) {
|
||||||
self,
|
|
||||||
shutdown: nym_task::TaskClient,
|
|
||||||
packet_type: PacketType,
|
|
||||||
) {
|
|
||||||
let mut acknowledgement_listener = self.acknowledgement_listener;
|
let mut acknowledgement_listener = self.acknowledgement_listener;
|
||||||
let mut input_message_listener = self.input_message_listener;
|
let mut input_message_listener = self.input_message_listener;
|
||||||
let mut retransmission_request_listener = self.retransmission_request_listener;
|
let mut retransmission_request_listener = self.retransmission_request_listener;
|
||||||
@@ -279,7 +275,7 @@ where
|
|||||||
let shutdown_handle = shutdown.clone();
|
let shutdown_handle = shutdown.clone();
|
||||||
spawn_future(async move {
|
spawn_future(async move {
|
||||||
retransmission_request_listener
|
retransmission_request_listener
|
||||||
.run_with_shutdown(shutdown_handle, packet_type)
|
.run_with_shutdown(shutdown_handle)
|
||||||
.await;
|
.await;
|
||||||
debug!("The retransmission request listener has finished execution!");
|
debug!("The retransmission request listener has finished execution!");
|
||||||
});
|
});
|
||||||
|
|||||||
+4
-10
@@ -48,20 +48,19 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
packet_recipient: Recipient,
|
packet_recipient: Recipient,
|
||||||
chunk_data: Fragment,
|
chunk_data: Fragment,
|
||||||
packet_type: PacketType,
|
|
||||||
) -> Result<PreparedFragment, PreparationError> {
|
) -> Result<PreparedFragment, PreparationError> {
|
||||||
debug!("retransmitting normal packet...");
|
debug!("retransmitting normal packet...");
|
||||||
|
|
||||||
// TODO: Figure out retransmission packet type signaling
|
// TODO: Figure out retransmission packet type signaling
|
||||||
|
|
||||||
self.message_handler
|
self.message_handler
|
||||||
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, packet_type)
|
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, PacketType::Mix)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_retransmission_request(
|
async fn on_retransmission_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
weak_timed_out_ack: Weak<PendingAcknowledgement>,
|
weak_timed_out_ack: Weak<PendingAcknowledgement>,
|
||||||
packet_type: PacketType,
|
|
||||||
) {
|
) {
|
||||||
let timed_out_ack = match weak_timed_out_ack.upgrade() {
|
let timed_out_ack = match weak_timed_out_ack.upgrade() {
|
||||||
Some(timed_out_ack) => timed_out_ack,
|
Some(timed_out_ack) => timed_out_ack,
|
||||||
@@ -88,7 +87,6 @@ where
|
|||||||
self.prepare_normal_retransmission_chunk(
|
self.prepare_normal_retransmission_chunk(
|
||||||
**recipient,
|
**recipient,
|
||||||
timed_out_ack.message_chunk.clone(),
|
timed_out_ack.message_chunk.clone(),
|
||||||
packet_type,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -144,17 +142,13 @@ where
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn run_with_shutdown(
|
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
|
||||||
&mut self,
|
|
||||||
mut shutdown: nym_task::TaskClient,
|
|
||||||
packet_type: PacketType,
|
|
||||||
) {
|
|
||||||
debug!("Started RetransmissionRequestListener with graceful shutdown support");
|
debug!("Started RetransmissionRequestListener with graceful shutdown support");
|
||||||
|
|
||||||
while !shutdown.is_shutdown() {
|
while !shutdown.is_shutdown() {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
timed_out_ack = self.request_receiver.next() => match timed_out_ack {
|
timed_out_ack = self.request_receiver.next() => match timed_out_ack {
|
||||||
Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack, packet_type).await,
|
Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack).await,
|
||||||
None => {
|
None => {
|
||||||
log::trace!("RetransmissionRequestListener: Stopping since channel closed");
|
log::trace!("RetransmissionRequestListener: Stopping since channel closed");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use std::time::Duration;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
// TODO: move that error elsewhere since it seems to be contaminating different files
|
// TODO: move that error elsewhere since it seems to be contaminating different files
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum PreparationError {
|
pub enum PreparationError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NymTopologyError(#[from] NymTopologyError),
|
NymTopologyError(#[from] NymTopologyError),
|
||||||
@@ -431,7 +431,6 @@ where
|
|||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
) -> Result<(), PreparationError> {
|
) -> Result<(), PreparationError> {
|
||||||
debug!("Sending non-reply message with packet type {packet_type}");
|
|
||||||
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised
|
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised
|
||||||
debug_assert!(!matches!(message, NymMessage::Reply(_)));
|
debug_assert!(!matches!(message, NymMessage::Reply(_)));
|
||||||
|
|
||||||
@@ -439,11 +438,7 @@ where
|
|||||||
let topology_permit = self.topology_access.get_read_permit().await;
|
let topology_permit = self.topology_access.get_read_permit().await;
|
||||||
let topology = self.get_topology(&topology_permit)?;
|
let topology = self.get_topology(&topology_permit)?;
|
||||||
|
|
||||||
let packet_size = if packet_type == PacketType::Outfox {
|
let packet_size = self.optimal_packet_size(&message);
|
||||||
PacketSize::OutfoxRegularPacket
|
|
||||||
} else {
|
|
||||||
self.optimal_packet_size(&message)
|
|
||||||
};
|
|
||||||
debug!("Using {packet_size} packets for {message}");
|
debug!("Using {packet_size} packets for {message}");
|
||||||
let fragments = self
|
let fragments = self
|
||||||
.message_preparer
|
.message_preparer
|
||||||
@@ -460,7 +455,7 @@ where
|
|||||||
topology,
|
topology,
|
||||||
&self.config.ack_key,
|
&self.config.ack_key,
|
||||||
&recipient,
|
&recipient,
|
||||||
packet_type,
|
&packet_type,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let real_message = RealMessage::new(
|
let real_message = RealMessage::new(
|
||||||
@@ -486,7 +481,6 @@ where
|
|||||||
amount: u32,
|
amount: u32,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
) -> Result<(), PreparationError> {
|
) -> Result<(), PreparationError> {
|
||||||
debug!("Sending additional reply SURBs with packet type {packet_type}");
|
|
||||||
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
||||||
let (reply_surbs, reply_keys) =
|
let (reply_surbs, reply_keys) =
|
||||||
self.generate_reply_surbs_with_keys(amount as usize).await?;
|
self.generate_reply_surbs_with_keys(amount as usize).await?;
|
||||||
@@ -518,7 +512,6 @@ where
|
|||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
) -> Result<(), SurbWrappedPreparationError> {
|
) -> Result<(), SurbWrappedPreparationError> {
|
||||||
debug!("Sending message with reply SURBs with packet type {packet_type}");
|
|
||||||
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
||||||
let (reply_surbs, reply_keys) = self
|
let (reply_surbs, reply_keys) = self
|
||||||
.generate_reply_surbs_with_keys(num_reply_surbs as usize)
|
.generate_reply_surbs_with_keys(num_reply_surbs as usize)
|
||||||
@@ -542,7 +535,6 @@ where
|
|||||||
chunk: Fragment,
|
chunk: Fragment,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
) -> Result<PreparedFragment, PreparationError> {
|
) -> Result<PreparedFragment, PreparationError> {
|
||||||
debug!("Sending single chunk with packet type {packet_type}");
|
|
||||||
let topology_permit = self.topology_access.get_read_permit().await;
|
let topology_permit = self.topology_access.get_read_permit().await;
|
||||||
let topology = self.get_topology(&topology_permit)?;
|
let topology = self.get_topology(&topology_permit)?;
|
||||||
|
|
||||||
@@ -553,7 +545,7 @@ where
|
|||||||
topology,
|
topology,
|
||||||
&self.config.ack_key,
|
&self.config.ack_key,
|
||||||
&recipient,
|
&recipient,
|
||||||
packet_type,
|
&packet_type,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -565,7 +557,7 @@ where
|
|||||||
fragments: Vec<Fragment>,
|
fragments: Vec<Fragment>,
|
||||||
reply_surbs: Vec<ReplySurb>,
|
reply_surbs: Vec<ReplySurb>,
|
||||||
) -> Result<Vec<PreparedFragment>, SurbWrappedPreparationError> {
|
) -> Result<Vec<PreparedFragment>, SurbWrappedPreparationError> {
|
||||||
debug_assert_eq!(
|
debug_assert_ne!(
|
||||||
fragments.len(),
|
fragments.len(),
|
||||||
reply_surbs.len(),
|
reply_surbs.len(),
|
||||||
"attempted to send {} fragments with {} reply surbs",
|
"attempted to send {} fragments with {} reply surbs",
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ use log::*;
|
|||||||
use nym_gateway_client::AcknowledgementReceiver;
|
use nym_gateway_client::AcknowledgementReceiver;
|
||||||
use nym_sphinx::acknowledgements::AckKey;
|
use nym_sphinx::acknowledgements::AckKey;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::params::PacketType;
|
|
||||||
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
|
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
|
||||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -208,7 +207,7 @@ impl RealMessagesController<OsRng> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_with_shutdown(self, shutdown: nym_task::TaskClient, packet_type: PacketType) {
|
pub fn start_with_shutdown(self, shutdown: nym_task::TaskClient) {
|
||||||
let mut out_queue_control = self.out_queue_control;
|
let mut out_queue_control = self.out_queue_control;
|
||||||
let ack_control = self.ack_control;
|
let ack_control = self.ack_control;
|
||||||
let mut reply_control = self.reply_control;
|
let mut reply_control = self.reply_control;
|
||||||
@@ -224,6 +223,6 @@ impl RealMessagesController<OsRng> {
|
|||||||
debug!("The reply controller has finished execution!");
|
debug!("The reply controller has finished execution!");
|
||||||
});
|
});
|
||||||
|
|
||||||
ack_control.start_with_shutdown(shutdown, packet_type);
|
ack_control.start_with_shutdown(shutdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use self::sending_delay_controller::SendingDelayController;
|
use self::sending_delay_controller::SendingDelayController;
|
||||||
@@ -247,7 +247,6 @@ where
|
|||||||
self.config.average_ack_delay,
|
self.config.average_ack_delay,
|
||||||
self.config.traffic.average_packet_delay,
|
self.config.traffic.average_packet_delay,
|
||||||
cover_traffic_packet_size,
|
cover_traffic_packet_size,
|
||||||
self.config.traffic.packet_type,
|
|
||||||
)
|
)
|
||||||
.expect(
|
.expect(
|
||||||
"Somehow failed to generate a loop cover message with a valid topology",
|
"Somehow failed to generate a loop cover message with a valid topology",
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use crate::client::replies::reply_storage::backend::Empty;
|
|||||||
use crate::client::replies::reply_storage::{CombinedReplyStorage, ReplyStorageBackend};
|
use crate::client::replies::reply_storage::{CombinedReplyStorage, ReplyStorageBackend};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// well, right now we don't have the browser storage : (
|
// well, right now we don't have the browser storage : (
|
||||||
// so we keep everything in memory
|
// so we keep everything in memory
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -27,6 +29,22 @@ impl Backend {
|
|||||||
impl ReplyStorageBackend for Backend {
|
impl ReplyStorageBackend for Backend {
|
||||||
type StorageError = <Empty as ReplyStorageBackend>::StorageError;
|
type StorageError = <Empty as ReplyStorageBackend>::StorageError;
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
debug_config: &crate::config::DebugConfig,
|
||||||
|
_db_path: Option<PathBuf>,
|
||||||
|
) -> Result<Self, Self::StorageError> {
|
||||||
|
Ok(Backend {
|
||||||
|
empty: Empty {
|
||||||
|
min_surb_threshold: debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
max_surb_threshold: debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn flush_surb_storage(
|
async fn flush_surb_storage(
|
||||||
&mut self,
|
&mut self,
|
||||||
storage: &CombinedReplyStorage,
|
storage: &CombinedReplyStorage,
|
||||||
@@ -41,4 +59,8 @@ impl ReplyStorageBackend for Backend {
|
|||||||
async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||||
self.empty.load_surb_storage().await
|
self.empty.load_surb_storage().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||||
|
self.empty.get_inactive_storage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::base_client::non_wasm_helpers;
|
||||||
use crate::client::replies::reply_storage::backend::fs_backend::manager::StorageManager;
|
use crate::client::replies::reply_storage::backend::fs_backend::manager::StorageManager;
|
||||||
use crate::client::replies::reply_storage::backend::fs_backend::models::{
|
use crate::client::replies::reply_storage::backend::fs_backend::models::{
|
||||||
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
|
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
|
||||||
@@ -22,11 +23,49 @@ mod error;
|
|||||||
mod manager;
|
mod manager;
|
||||||
mod models;
|
mod models;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum StorageManagerState {
|
||||||
|
Storage(StorageManager),
|
||||||
|
Inactive(InactiveMetadata),
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the storage backaed is initialized as inactive, it will still contain metadata parameters
|
||||||
|
// that will be needed when the in-mem storage is fetched for use.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct InactiveMetadata {
|
||||||
|
pub minimum_reply_surb_storage_threshold: usize,
|
||||||
|
pub maximum_reply_surb_storage_threshold: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageManagerState {
|
||||||
|
fn get(&self) -> &StorageManager {
|
||||||
|
match self {
|
||||||
|
StorageManagerState::Storage(manager) => manager,
|
||||||
|
StorageManagerState::Inactive(_) => {
|
||||||
|
panic!("tried to get storage of an inactive backend")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut(&mut self) -> &mut StorageManager {
|
||||||
|
match self {
|
||||||
|
StorageManagerState::Storage(manager) => manager,
|
||||||
|
StorageManagerState::Inactive(_) => {
|
||||||
|
panic!("tried to get storage of an inactive backend")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
matches!(self, StorageManagerState::Storage(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
temporary_old_path: Option<PathBuf>,
|
temporary_old_path: Option<PathBuf>,
|
||||||
database_path: PathBuf,
|
database_path: PathBuf,
|
||||||
manager: StorageManager,
|
manager: StorageManagerState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
@@ -46,12 +85,26 @@ impl Backend {
|
|||||||
let backend = Backend {
|
let backend = Backend {
|
||||||
temporary_old_path: None,
|
temporary_old_path: None,
|
||||||
database_path: owned_path,
|
database_path: owned_path,
|
||||||
manager,
|
manager: StorageManagerState::Storage(manager),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(backend)
|
Ok(backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_inactive(
|
||||||
|
minimum_reply_surb_storage_threshold: usize,
|
||||||
|
maximum_reply_surb_storage_threshold: usize,
|
||||||
|
) -> Self {
|
||||||
|
Backend {
|
||||||
|
temporary_old_path: None,
|
||||||
|
database_path: PathBuf::new(),
|
||||||
|
manager: StorageManagerState::Inactive(InactiveMetadata {
|
||||||
|
minimum_reply_surb_storage_threshold,
|
||||||
|
maximum_reply_surb_storage_threshold,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn try_load<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
|
pub async fn try_load<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
|
||||||
let owned_path: PathBuf = database_path.as_ref().into();
|
let owned_path: PathBuf = database_path.as_ref().into();
|
||||||
if owned_path.file_name().is_none() {
|
if owned_path.file_name().is_none() {
|
||||||
@@ -123,13 +176,12 @@ impl Backend {
|
|||||||
Ok(Backend {
|
Ok(Backend {
|
||||||
temporary_old_path: None,
|
temporary_old_path: None,
|
||||||
database_path: owned_path,
|
database_path: owned_path,
|
||||||
// manager: StorageManagerState::Storage(manager),
|
manager: StorageManagerState::Storage(manager),
|
||||||
manager,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn close_pool(&mut self) {
|
async fn close_pool(&mut self) {
|
||||||
self.manager.connection_pool.close().await;
|
self.manager.get_mut().connection_pool.close().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn rotate(&mut self) -> Result<(), StorageError> {
|
async fn rotate(&mut self) -> Result<(), StorageError> {
|
||||||
@@ -148,8 +200,9 @@ impl Backend {
|
|||||||
|
|
||||||
fs::rename(&self.database_path, &temp_old)
|
fs::rename(&self.database_path, &temp_old)
|
||||||
.map_err(|err| StorageError::DatabaseRenameError { source: err })?;
|
.map_err(|err| StorageError::DatabaseRenameError { source: err })?;
|
||||||
self.manager = StorageManager::init(&self.database_path, true).await?;
|
self.manager =
|
||||||
self.manager.create_status_table().await?;
|
StorageManagerState::Storage(StorageManager::init(&self.database_path, true).await?);
|
||||||
|
self.manager.get_mut().create_status_table().await?;
|
||||||
|
|
||||||
self.temporary_old_path = Some(temp_old);
|
self.temporary_old_path = Some(temp_old);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -166,26 +219,27 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn start_storage_flush(&self) -> Result<(), StorageError> {
|
async fn start_storage_flush(&self) -> Result<(), StorageError> {
|
||||||
Ok(self.manager.set_flush_status(true).await?)
|
Ok(self.manager.get().set_flush_status(true).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn end_storage_flush(&self) -> Result<(), StorageError> {
|
async fn end_storage_flush(&self) -> Result<(), StorageError> {
|
||||||
self.manager
|
self.manager
|
||||||
|
.get()
|
||||||
.set_previous_flush_timestamp(OffsetDateTime::now_utc().unix_timestamp())
|
.set_previous_flush_timestamp(OffsetDateTime::now_utc().unix_timestamp())
|
||||||
.await?;
|
.await?;
|
||||||
Ok(self.manager.set_flush_status(false).await?)
|
Ok(self.manager.get().set_flush_status(false).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_client_use(&self) -> Result<(), StorageError> {
|
async fn start_client_use(&self) -> Result<(), StorageError> {
|
||||||
Ok(self.manager.set_client_in_use_status(true).await?)
|
Ok(self.manager.get().set_client_in_use_status(true).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stop_client_use(&self) -> Result<(), StorageError> {
|
async fn stop_client_use(&self) -> Result<(), StorageError> {
|
||||||
Ok(self.manager.set_client_in_use_status(false).await?)
|
Ok(self.manager.get().set_client_in_use_status(false).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_stored_tags(&self) -> Result<UsedSenderTags, StorageError> {
|
async fn get_stored_tags(&self) -> Result<UsedSenderTags, StorageError> {
|
||||||
let stored = self.manager.get_tags().await?;
|
let stored = self.manager.get().get_tags().await?;
|
||||||
|
|
||||||
// stop at the first instance of corruption. if even a single entry is malformed,
|
// stop at the first instance of corruption. if even a single entry is malformed,
|
||||||
// something weird has happened and we can't trust the rest of the data
|
// something weird has happened and we can't trust the rest of the data
|
||||||
@@ -201,6 +255,7 @@ impl Backend {
|
|||||||
for map_ref in tags.as_raw_iter() {
|
for map_ref in tags.as_raw_iter() {
|
||||||
let (recipient, tag) = map_ref.pair();
|
let (recipient, tag) = map_ref.pair();
|
||||||
self.manager
|
self.manager
|
||||||
|
.get()
|
||||||
.insert_tag(StoredSenderTag::new(*recipient, *tag))
|
.insert_tag(StoredSenderTag::new(*recipient, *tag))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@@ -208,7 +263,7 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_stored_reply_keys(&self) -> Result<SentReplyKeys, StorageError> {
|
async fn get_stored_reply_keys(&self) -> Result<SentReplyKeys, StorageError> {
|
||||||
let stored = self.manager.get_reply_keys().await?;
|
let stored = self.manager.get().get_reply_keys().await?;
|
||||||
|
|
||||||
// stop at the first instance of corruption. if even a single entry is malformed,
|
// stop at the first instance of corruption. if even a single entry is malformed,
|
||||||
// something weird has happened and we can't trust the rest of the data
|
// something weird has happened and we can't trust the rest of the data
|
||||||
@@ -224,6 +279,7 @@ impl Backend {
|
|||||||
for map_ref in reply_keys.as_raw_iter() {
|
for map_ref in reply_keys.as_raw_iter() {
|
||||||
let (digest, key) = map_ref.pair();
|
let (digest, key) = map_ref.pair();
|
||||||
self.manager
|
self.manager
|
||||||
|
.get()
|
||||||
.insert_reply_key(StoredReplyKey::new(*digest, *key))
|
.insert_reply_key(StoredReplyKey::new(*digest, *key))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@@ -231,7 +287,7 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_stored_reply_surbs(&self) -> Result<ReceivedReplySurbsMap, StorageError> {
|
async fn get_stored_reply_surbs(&self) -> Result<ReceivedReplySurbsMap, StorageError> {
|
||||||
let surb_senders = self.manager.get_surb_senders().await?;
|
let surb_senders = self.manager.get().get_surb_senders().await?;
|
||||||
|
|
||||||
let metadata = self.get_reply_surb_storage_metadata().await?;
|
let metadata = self.get_reply_surb_storage_metadata().await?;
|
||||||
let mut received_surbs = Vec::with_capacity(surb_senders.len());
|
let mut received_surbs = Vec::with_capacity(surb_senders.len());
|
||||||
@@ -241,6 +297,7 @@ impl Backend {
|
|||||||
sender.try_into()?;
|
sender.try_into()?;
|
||||||
let stored_surbs = self
|
let stored_surbs = self
|
||||||
.manager
|
.manager
|
||||||
|
.get()
|
||||||
.get_reply_surbs(sender_id)
|
.get_reply_surbs(sender_id)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -268,6 +325,7 @@ impl Backend {
|
|||||||
let (tag, received_surbs) = map_ref.pair();
|
let (tag, received_surbs) = map_ref.pair();
|
||||||
let sender_id = self
|
let sender_id = self
|
||||||
.manager
|
.manager
|
||||||
|
.get()
|
||||||
.insert_surb_sender(StoredSurbSender::new(
|
.insert_surb_sender(StoredSurbSender::new(
|
||||||
*tag,
|
*tag,
|
||||||
received_surbs.surbs_last_received_at(),
|
received_surbs.surbs_last_received_at(),
|
||||||
@@ -276,6 +334,7 @@ impl Backend {
|
|||||||
|
|
||||||
for reply_surb in received_surbs.surbs_ref() {
|
for reply_surb in received_surbs.surbs_ref() {
|
||||||
self.manager
|
self.manager
|
||||||
|
.get()
|
||||||
.insert_reply_surb(StoredReplySurb::new(sender_id, reply_surb))
|
.insert_reply_surb(StoredReplySurb::new(sender_id, reply_surb))
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
@@ -287,6 +346,7 @@ impl Backend {
|
|||||||
&self,
|
&self,
|
||||||
) -> Result<ReplySurbStorageMetadata, StorageError> {
|
) -> Result<ReplySurbStorageMetadata, StorageError> {
|
||||||
self.manager
|
self.manager
|
||||||
|
.get()
|
||||||
.get_reply_surb_storage_metadata()
|
.get_reply_surb_storage_metadata()
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
@@ -297,6 +357,7 @@ impl Backend {
|
|||||||
reply_surbs: &ReceivedReplySurbsMap,
|
reply_surbs: &ReceivedReplySurbsMap,
|
||||||
) -> Result<(), StorageError> {
|
) -> Result<(), StorageError> {
|
||||||
self.manager
|
self.manager
|
||||||
|
.get()
|
||||||
.insert_reply_surb_storage_metadata(ReplySurbStorageMetadata::new(
|
.insert_reply_surb_storage_metadata(ReplySurbStorageMetadata::new(
|
||||||
reply_surbs.min_surb_threshold(),
|
reply_surbs.min_surb_threshold(),
|
||||||
reply_surbs.max_surb_threshold(),
|
reply_surbs.max_surb_threshold(),
|
||||||
@@ -310,6 +371,24 @@ impl Backend {
|
|||||||
impl ReplyStorageBackend for Backend {
|
impl ReplyStorageBackend for Backend {
|
||||||
type StorageError = error::StorageError;
|
type StorageError = error::StorageError;
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
debug_config: &crate::config::DebugConfig,
|
||||||
|
db_path: Option<PathBuf>,
|
||||||
|
) -> Result<Self, Self::StorageError> {
|
||||||
|
non_wasm_helpers::setup_fs_reply_surb_backend(db_path, debug_config)
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!("Failed to create storage: {err}");
|
||||||
|
Self::StorageError::FailedToCreateStorage {
|
||||||
|
source: Box::new(err),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
self.manager.is_active()
|
||||||
|
}
|
||||||
|
|
||||||
async fn start_storage_session(&self) -> Result<(), Self::StorageError> {
|
async fn start_storage_session(&self) -> Result<(), Self::StorageError> {
|
||||||
self.start_client_use().await
|
self.start_client_use().await
|
||||||
}
|
}
|
||||||
@@ -347,6 +426,18 @@ impl ReplyStorageBackend for Backend {
|
|||||||
Ok(CombinedReplyStorage::load(reply_keys, reply_surbs, tags))
|
Ok(CombinedReplyStorage::load(reply_keys, reply_surbs, tags))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||||
|
match self.manager {
|
||||||
|
StorageManagerState::Storage(_) => {
|
||||||
|
panic!("tried to get inactive storage from an active storage backend")
|
||||||
|
}
|
||||||
|
StorageManagerState::Inactive(ref state) => Ok(CombinedReplyStorage::new(
|
||||||
|
state.minimum_reply_surb_storage_threshold,
|
||||||
|
state.maximum_reply_surb_storage_threshold,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn stop_storage_session(self) -> Result<(), Self::StorageError> {
|
async fn stop_storage_session(self) -> Result<(), Self::StorageError> {
|
||||||
self.stop_client_use().await
|
self.stop_client_use().await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::{error::Error, path::PathBuf};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@@ -26,19 +26,24 @@ pub struct Empty {
|
|||||||
pub max_surb_threshold: usize,
|
pub max_surb_threshold: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Empty {
|
|
||||||
fn default() -> Self {
|
|
||||||
Empty {
|
|
||||||
min_surb_threshold: 20,
|
|
||||||
max_surb_threshold: 200,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ReplyStorageBackend for Empty {
|
impl ReplyStorageBackend for Empty {
|
||||||
type StorageError = UndefinedError;
|
type StorageError = UndefinedError;
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
debug_config: &crate::config::DebugConfig,
|
||||||
|
_db_path: Option<PathBuf>,
|
||||||
|
) -> Result<Self, Self::StorageError> {
|
||||||
|
Ok(Self {
|
||||||
|
min_surb_threshold: debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
max_surb_threshold: debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn flush_surb_storage(
|
async fn flush_surb_storage(
|
||||||
&mut self,
|
&mut self,
|
||||||
_storage: &CombinedReplyStorage,
|
_storage: &CombinedReplyStorage,
|
||||||
@@ -59,12 +64,28 @@ impl ReplyStorageBackend for Empty {
|
|||||||
self.max_surb_threshold,
|
self.max_surb_threshold,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||||
|
Ok(CombinedReplyStorage::new(
|
||||||
|
self.min_surb_threshold,
|
||||||
|
self.max_surb_threshold,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ReplyStorageBackend: Sized {
|
pub trait ReplyStorageBackend: Sized {
|
||||||
type StorageError: Error + 'static;
|
type StorageError: Error + 'static;
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
debug_config: &crate::config::DebugConfig,
|
||||||
|
db_path: Option<PathBuf>,
|
||||||
|
) -> Result<Self, Self::StorageError>;
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn start_storage_session(&self) -> Result<(), Self::StorageError> {
|
async fn start_storage_session(&self) -> Result<(), Self::StorageError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -82,6 +103,11 @@ pub trait ReplyStorageBackend: Sized {
|
|||||||
|
|
||||||
async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError>;
|
async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError>;
|
||||||
|
|
||||||
|
/// In the case the storage backend is initialized in an inactive state (persisting data is
|
||||||
|
/// disabled), we might still need to fetch the (in-mem) storage and the parameters it was
|
||||||
|
/// created with.
|
||||||
|
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError>;
|
||||||
|
|
||||||
async fn stop_storage_session(self) -> Result<(), Self::StorageError> {
|
async fn stop_storage_session(self) -> Result<(), Self::StorageError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl TopologyRefresherConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TopologyRefresher {
|
pub struct TopologyRefresher {
|
||||||
topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
topology_provider: Box<dyn TopologyProvider>,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
|
|
||||||
refresh_rate: Duration,
|
refresh_rate: Duration,
|
||||||
@@ -37,7 +37,7 @@ impl TopologyRefresher {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
cfg: TopologyRefresherConfig,
|
cfg: TopologyRefresherConfig,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
topology_provider: Box<dyn TopologyProvider>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
TopologyRefresher {
|
TopologyRefresher {
|
||||||
topology_provider,
|
topology_provider,
|
||||||
@@ -47,7 +47,7 @@ impl TopologyRefresher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_topology_provider(&mut self, provider: Box<dyn TopologyProvider + Send + Sync>) {
|
pub fn change_topology_provider(&mut self, provider: Box<dyn TopologyProvider>) {
|
||||||
self.topology_provider = provider;
|
self.topology_provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
pub const DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME: &str = "private_identity.pem";
|
|
||||||
pub const DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME: &str = "public_identity.pem";
|
|
||||||
pub const DEFAULT_PRIVATE_ENCRYPTION_KEY_FILENAME: &str = "private_encryption.pem";
|
|
||||||
pub const DEFAULT_PUBLIC_ENCRYPTION_KEY_FILENAME: &str = "public_encryption.pem";
|
|
||||||
pub const DEFAULT_GATEWAY_SHARED_KEY_FILENAME: &str = "gateway_shared.pem";
|
|
||||||
pub const DEFAULT_ACK_KEY_FILENAME: &str = "ack_key.pem";
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
pub struct ClientKeysPaths {
|
|
||||||
/// Path to file containing private identity key.
|
|
||||||
pub private_identity_key_file: PathBuf,
|
|
||||||
|
|
||||||
/// Path to file containing public identity key.
|
|
||||||
pub public_identity_key_file: PathBuf,
|
|
||||||
|
|
||||||
/// Path to file containing private encryption key.
|
|
||||||
pub private_encryption_key_file: PathBuf,
|
|
||||||
|
|
||||||
/// Path to file containing public encryption key.
|
|
||||||
pub public_encryption_key_file: PathBuf,
|
|
||||||
|
|
||||||
/// Path to file containing shared key derived with the specified gateway that is used
|
|
||||||
/// for all communication with it.
|
|
||||||
pub gateway_shared_key_file: PathBuf,
|
|
||||||
|
|
||||||
/// Path to file containing key used for encrypting and decrypting the content of an
|
|
||||||
/// acknowledgement so that nobody besides the client knows which packet it refers to.
|
|
||||||
pub ack_key_file: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientKeysPaths {
|
|
||||||
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
|
|
||||||
let base_dir = base_data_directory.as_ref();
|
|
||||||
|
|
||||||
ClientKeysPaths {
|
|
||||||
private_identity_key_file: base_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
|
|
||||||
public_identity_key_file: base_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
|
|
||||||
private_encryption_key_file: base_dir.join(DEFAULT_PRIVATE_ENCRYPTION_KEY_FILENAME),
|
|
||||||
public_encryption_key_file: base_dir.join(DEFAULT_PUBLIC_ENCRYPTION_KEY_FILENAME),
|
|
||||||
gateway_shared_key_file: base_dir.join(DEFAULT_GATEWAY_SHARED_KEY_FILENAME),
|
|
||||||
ack_key_file: base_dir.join(DEFAULT_ACK_KEY_FILENAME),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn identity_key_pair_path(&self) -> nym_pemstore::KeyPairPath {
|
|
||||||
nym_pemstore::KeyPairPath::new(
|
|
||||||
self.private_identity_key().to_path_buf(),
|
|
||||||
self.public_identity_key().to_path_buf(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encryption_key_pair_path(&self) -> nym_pemstore::KeyPairPath {
|
|
||||||
nym_pemstore::KeyPairPath::new(
|
|
||||||
self.private_encryption_key().to_path_buf(),
|
|
||||||
self.public_encryption_key().to_path_buf(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn any_file_exists(&self) -> bool {
|
|
||||||
matches!(self.public_identity_key_file.try_exists(), Ok(true))
|
|
||||||
|| matches!(self.private_identity_key_file.try_exists(), Ok(true))
|
|
||||||
|| matches!(self.public_encryption_key_file.try_exists(), Ok(true))
|
|
||||||
|| matches!(self.private_encryption_key_file.try_exists(), Ok(true))
|
|
||||||
|| matches!(self.gateway_shared_key_file.try_exists(), Ok(true))
|
|
||||||
|| matches!(self.ack_key_file.try_exists(), Ok(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn any_file_exists_and_return(&self) -> Option<PathBuf> {
|
|
||||||
file_exists(&self.public_identity_key_file)
|
|
||||||
.or_else(|| file_exists(&self.private_identity_key_file))
|
|
||||||
.or_else(|| file_exists(&self.public_encryption_key_file))
|
|
||||||
.or_else(|| file_exists(&self.private_encryption_key_file))
|
|
||||||
.or_else(|| file_exists(&self.gateway_shared_key_file))
|
|
||||||
.or_else(|| file_exists(&self.ack_key_file))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gateway_key_file_exists(&self) -> bool {
|
|
||||||
matches!(self.gateway_shared_key_file.try_exists(), Ok(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn private_identity_key(&self) -> &Path {
|
|
||||||
&self.private_identity_key_file
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn public_identity_key(&self) -> &Path {
|
|
||||||
&self.public_identity_key_file
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn private_encryption_key(&self) -> &Path {
|
|
||||||
&self.private_encryption_key_file
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn public_encryption_key(&self) -> &Path {
|
|
||||||
&self.public_encryption_key_file
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gateway_shared_key(&self) -> &Path {
|
|
||||||
&self.gateway_shared_key_file
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ack_key(&self) -> &Path {
|
|
||||||
&self.ack_key_file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn file_exists(path: &Path) -> Option<PathBuf> {
|
|
||||||
if matches!(path.try_exists(), Ok(true)) {
|
|
||||||
return Some(path.to_path_buf());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
pub mod keys_paths;
|
|
||||||
pub mod old_v1_1_20_2;
|
|
||||||
|
|
||||||
pub const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
|
|
||||||
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
|
|
||||||
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct CommonClientPaths {
|
|
||||||
pub keys: ClientKeysPaths,
|
|
||||||
|
|
||||||
/// Path to the file containing information about gateway used by this client,
|
|
||||||
/// i.e. details such as its public key, owner address or the network information.
|
|
||||||
pub gateway_details: PathBuf,
|
|
||||||
|
|
||||||
/// Path to the database containing bandwidth credentials of this client.
|
|
||||||
pub credentials_database: PathBuf,
|
|
||||||
|
|
||||||
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
|
||||||
pub reply_surb_database: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommonClientPaths {
|
|
||||||
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
|
|
||||||
let base_dir = base_data_directory.as_ref();
|
|
||||||
|
|
||||||
CommonClientPaths {
|
|
||||||
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
|
|
||||||
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
|
|
||||||
gateway_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
|
|
||||||
keys: ClientKeysPaths::new_default(base_data_directory),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
|
|
||||||
use crate::config::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAY_DETAILS_FILENAME};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct CommonClientPathsV1_1_20_2 {
|
|
||||||
pub keys: ClientKeysPaths,
|
|
||||||
pub credentials_database: PathBuf,
|
|
||||||
pub reply_surb_database: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommonClientPathsV1_1_20_2 {
|
|
||||||
pub fn upgrade_default(self) -> CommonClientPaths {
|
|
||||||
let data_dir = self
|
|
||||||
.reply_surb_database
|
|
||||||
.parent()
|
|
||||||
.expect("client paths upgrade failure");
|
|
||||||
CommonClientPaths {
|
|
||||||
keys: self.keys,
|
|
||||||
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
|
|
||||||
credentials_database: self.credentials_database,
|
|
||||||
reply_surb_database: self.reply_surb_database,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,22 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use nym_config::defaults::NymNetworkDetails;
|
use nym_config::defaults::NymNetworkDetails;
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_config::{NymConfig, OptionalSet, CRED_DB_FILE_NAME};
|
||||||
use nym_sphinx::params::{PacketSize, PacketType};
|
use nym_sphinx::params::{PacketSize, PacketType};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::error::ClientCoreError;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
pub mod disk_persistence;
|
|
||||||
pub mod old_config_v1_1_13;
|
pub mod old_config_v1_1_13;
|
||||||
pub mod old_config_v1_1_20;
|
pub mod persistence;
|
||||||
pub mod old_config_v1_1_20_2;
|
|
||||||
|
pub const MISSING_VALUE: &str = "MISSING VALUE";
|
||||||
|
|
||||||
// 'DEBUG'
|
// 'DEBUG'
|
||||||
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
||||||
@@ -56,41 +57,141 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
|
|||||||
// 24 hours
|
// 24 hours
|
||||||
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
|
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
pub fn missing_string_value() -> String {
|
||||||
#[serde(deny_unknown_fields)]
|
MISSING_VALUE.to_string()
|
||||||
pub struct Config {
|
|
||||||
pub client: Client,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub debug: DebugConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
pub trait ClientCoreConfigTrait {
|
||||||
pub fn new<S: Into<String>>(id: S, version: S) -> Self {
|
fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig;
|
||||||
Config {
|
}
|
||||||
client: Client::new_default(id, version),
|
|
||||||
debug: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_client_config(client: Client, debug: DebugConfig) -> Self {
|
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||||
Config { client, debug }
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Config<T> {
|
||||||
|
client: Client<T>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
logging: Logging,
|
||||||
|
#[serde(default)]
|
||||||
|
debug: DebugConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ClientCoreConfigTrait for Config<T> {
|
||||||
|
fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig {
|
||||||
|
&self.client.gateway_endpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OptionalSet for Config<T> where T: NymConfig {}
|
||||||
|
|
||||||
|
impl<T> Config<T> {
|
||||||
|
pub fn new<S: Into<String>>(id: S) -> Self
|
||||||
|
where
|
||||||
|
T: NymConfig,
|
||||||
|
{
|
||||||
|
Config::default().with_id(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&self) -> bool {
|
pub fn validate(&self) -> bool {
|
||||||
|
// no other sections have explicit requirements (yet)
|
||||||
self.debug.validate()
|
self.debug.validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
|
#[must_use]
|
||||||
self.debug = debug;
|
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self
|
||||||
|
where
|
||||||
|
T: NymConfig,
|
||||||
|
{
|
||||||
|
self.client.id = id.into();
|
||||||
|
self.set_empty_fields_to_defaults();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_empty_fields_to_defaults(&mut self) -> bool
|
||||||
|
where
|
||||||
|
T: NymConfig,
|
||||||
|
{
|
||||||
|
let id = &self.client.id;
|
||||||
|
let mut changes_made = false;
|
||||||
|
|
||||||
|
// identity key setting
|
||||||
|
if self.client.private_identity_key_file.as_os_str().is_empty() {
|
||||||
|
changes_made = true;
|
||||||
|
self.client.private_identity_key_file =
|
||||||
|
self::Client::<T>::default_private_identity_key_file(id);
|
||||||
|
}
|
||||||
|
if self.client.public_identity_key_file.as_os_str().is_empty() {
|
||||||
|
changes_made = true;
|
||||||
|
self.client.public_identity_key_file =
|
||||||
|
self::Client::<T>::default_public_identity_key_file(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// encryption key setting
|
||||||
|
if self
|
||||||
|
.client
|
||||||
|
.private_encryption_key_file
|
||||||
|
.as_os_str()
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
|
changes_made = true;
|
||||||
|
self.client.private_encryption_key_file =
|
||||||
|
self::Client::<T>::default_private_encryption_key_file(id);
|
||||||
|
}
|
||||||
|
if self
|
||||||
|
.client
|
||||||
|
.public_encryption_key_file
|
||||||
|
.as_os_str()
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
|
changes_made = true;
|
||||||
|
self.client.public_encryption_key_file =
|
||||||
|
self::Client::<T>::default_public_encryption_key_file(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// shared gateway key setting
|
||||||
|
if self.client.gateway_shared_key_file.as_os_str().is_empty() {
|
||||||
|
changes_made = true;
|
||||||
|
self.client.gateway_shared_key_file =
|
||||||
|
self::Client::<T>::default_gateway_shared_key_file(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ack key setting
|
||||||
|
if self.client.ack_key_file.as_os_str().is_empty() {
|
||||||
|
changes_made = true;
|
||||||
|
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.client.reply_surb_database_path.as_os_str().is_empty() {
|
||||||
|
changes_made = true;
|
||||||
|
self.client.reply_surb_database_path =
|
||||||
|
self::Client::<T>::default_reply_surb_database_path(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.client.database_path.as_os_str().is_empty() {
|
||||||
|
changes_made = true;
|
||||||
|
self.client.database_path = self::Client::<T>::default_database_path(id);
|
||||||
|
}
|
||||||
|
changes_made
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
|
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
|
||||||
self.client.disabled_credentials_mode = disabled_credentials_mode;
|
self.client.disabled_credentials_mode = disabled_credentials_mode;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
|
||||||
|
self.client.gateway_endpoint = gateway_endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_gateway_endpoint(mut self, gateway_endpoint: GatewayEndpointConfig) -> Self {
|
||||||
|
self.client.gateway_endpoint = gateway_endpoint;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||||
|
self.client.gateway_endpoint.gateway_id = id.into();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
|
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
|
||||||
self.client.nyxd_urls = urls;
|
self.client.nyxd_urls = urls;
|
||||||
self
|
self
|
||||||
@@ -117,7 +218,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
|
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
|
||||||
self.debug.traffic.packet_type = packet_type;
|
self.client.packet_type = Some(packet_type);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +238,6 @@ impl Config {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_disabled_topology_refresh(mut self, disable_topology_refresh: bool) -> Self {
|
|
||||||
self.debug.topology.disable_refreshing = disable_topology_refresh;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_no_cover_traffic(&mut self) {
|
pub fn set_no_cover_traffic(&mut self) {
|
||||||
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
|
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
|
||||||
self.debug.traffic.disable_main_poisson_packet_distribution = true;
|
self.debug.traffic.disable_main_poisson_packet_distribution = true;
|
||||||
@@ -159,6 +255,34 @@ impl Config {
|
|||||||
self.client.disabled_credentials_mode
|
self.client.disabled_credentials_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_nym_root_directory(&self) -> PathBuf {
|
||||||
|
self.client.nym_root_directory.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_private_identity_key_file(&self) -> PathBuf {
|
||||||
|
self.client.private_identity_key_file.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_public_identity_key_file(&self) -> PathBuf {
|
||||||
|
self.client.public_identity_key_file.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_private_encryption_key_file(&self) -> PathBuf {
|
||||||
|
self.client.private_encryption_key_file.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_public_encryption_key_file(&self) -> PathBuf {
|
||||||
|
self.client.public_encryption_key_file.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_gateway_shared_key_file(&self) -> PathBuf {
|
||||||
|
self.client.gateway_shared_key_file.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ack_key_file(&self) -> PathBuf {
|
||||||
|
self.client.ack_key_file.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_validator_endpoints(&self) -> Vec<Url> {
|
pub fn get_validator_endpoints(&self) -> Vec<Url> {
|
||||||
self.client.nyxd_urls.clone()
|
self.client.nyxd_urls.clone()
|
||||||
}
|
}
|
||||||
@@ -166,6 +290,139 @@ impl Config {
|
|||||||
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
|
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
|
||||||
self.client.nym_api_urls.clone()
|
self.client.nym_api_urls.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_gateway_id(&self) -> String {
|
||||||
|
self.client.gateway_endpoint.gateway_id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_gateway_owner(&self) -> String {
|
||||||
|
self.client.gateway_endpoint.gateway_owner.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_gateway_listener(&self) -> String {
|
||||||
|
self.client.gateway_endpoint.gateway_listener.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_gateway_endpoint_config(&self) -> &GatewayEndpointConfig {
|
||||||
|
&self.client.gateway_endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_database_path(&self) -> PathBuf {
|
||||||
|
self.client.database_path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_reply_surb_database_path(&self) -> PathBuf {
|
||||||
|
self.client.reply_surb_database_path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_version(&self) -> &str {
|
||||||
|
&self.client.version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug getters
|
||||||
|
pub fn get_debug_config(&self) -> &DebugConfig {
|
||||||
|
&self.debug
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_average_packet_delay(&self) -> Duration {
|
||||||
|
self.debug.traffic.average_packet_delay
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_average_ack_delay(&self) -> Duration {
|
||||||
|
self.debug.acknowledgements.average_ack_delay
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ack_wait_multiplier(&self) -> f64 {
|
||||||
|
self.debug.acknowledgements.ack_wait_multiplier
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ack_wait_addition(&self) -> Duration {
|
||||||
|
self.debug.acknowledgements.ack_wait_addition
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_loop_cover_traffic_average_delay(&self) -> Duration {
|
||||||
|
self.debug.cover_traffic.loop_cover_traffic_average_delay
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_message_sending_average_delay(&self) -> Duration {
|
||||||
|
self.debug.traffic.message_sending_average_delay
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_gateway_response_timeout(&self) -> Duration {
|
||||||
|
self.debug.gateway_connection.gateway_response_timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_topology_refresh_rate(&self) -> Duration {
|
||||||
|
self.debug.topology.topology_refresh_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_topology_resolution_timeout(&self) -> Duration {
|
||||||
|
self.debug.topology.topology_resolution_timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_disabled_loop_cover_traffic_stream(&self) -> bool {
|
||||||
|
self.debug.cover_traffic.disable_loop_cover_traffic_stream
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_disabled_main_poisson_packet_distribution(&self) -> bool {
|
||||||
|
self.debug.traffic.disable_main_poisson_packet_distribution
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_minimum_reply_surb_storage_threshold(&self) -> usize {
|
||||||
|
self.debug.reply_surbs.minimum_reply_surb_storage_threshold
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maximum_reply_surb_storage_threshold(&self) -> usize {
|
||||||
|
self.debug.reply_surbs.maximum_reply_surb_storage_threshold
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_minimum_reply_surb_request_size(&self) -> u32 {
|
||||||
|
self.debug.reply_surbs.minimum_reply_surb_request_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maximum_reply_surb_request_size(&self) -> u32 {
|
||||||
|
self.debug.reply_surbs.maximum_reply_surb_request_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maximum_allowed_reply_surb_request_size(&self) -> u32 {
|
||||||
|
self.debug
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_allowed_reply_surb_request_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maximum_reply_surb_rerequest_waiting_period(&self) -> Duration {
|
||||||
|
self.debug
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_rerequest_waiting_period
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maximum_reply_surb_drop_waiting_period(&self) -> Duration {
|
||||||
|
self.debug
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_drop_waiting_period
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maximum_reply_surb_age(&self) -> Duration {
|
||||||
|
self.debug.reply_surbs.maximum_reply_surb_age
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maximum_reply_key_age(&self) -> Duration {
|
||||||
|
self.debug.reply_surbs.maximum_reply_key_age
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_packet_type(&self) -> PacketType {
|
||||||
|
self.client.packet_type.unwrap_or(PacketType::Mix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: NymConfig> Default for Config<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Config {
|
||||||
|
client: Client::<T>::default(),
|
||||||
|
logging: Default::default(),
|
||||||
|
debug: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
@@ -198,14 +455,6 @@ impl GatewayEndpointConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// separate block so it wouldn't be exported via wasm bindgen
|
|
||||||
impl GatewayEndpointConfig {
|
|
||||||
pub fn try_get_gateway_identity_key(&self) -> Result<identity::PublicKey, ClientCoreError> {
|
|
||||||
identity::PublicKey::from_base58_string(&self.gateway_id)
|
|
||||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
|
impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
|
||||||
fn from(node: nym_topology::gateway::Node) -> GatewayEndpointConfig {
|
fn from(node: nym_topology::gateway::Node) -> GatewayEndpointConfig {
|
||||||
let gateway_listener = node.clients_address();
|
let gateway_listener = node.clients_address();
|
||||||
@@ -218,10 +467,9 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
|
pub struct Client<T> {
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct Client {
|
|
||||||
/// Version of the client for which this configuration was created.
|
/// Version of the client for which this configuration was created.
|
||||||
|
#[serde(default = "missing_string_value")]
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
|
||||||
/// ID specifies the human readable ID of this particular client.
|
/// ID specifies the human readable ID of this particular client.
|
||||||
@@ -229,7 +477,6 @@ pub struct Client {
|
|||||||
|
|
||||||
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
|
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
|
||||||
/// to claim bandwidth without presenting bandwidth credentials.
|
/// to claim bandwidth without presenting bandwidth credentials.
|
||||||
// TODO: this should be moved to `debug.gateway_connection`
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub disabled_credentials_mode: bool,
|
pub disabled_credentials_mode: bool,
|
||||||
|
|
||||||
@@ -240,10 +487,52 @@ pub struct Client {
|
|||||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
#[serde(alias = "validator_api_urls")]
|
#[serde(alias = "validator_api_urls")]
|
||||||
pub nym_api_urls: Vec<Url>,
|
pub nym_api_urls: Vec<Url>,
|
||||||
|
|
||||||
|
/// Path to file containing private identity key.
|
||||||
|
pub private_identity_key_file: PathBuf,
|
||||||
|
|
||||||
|
/// Path to file containing public identity key.
|
||||||
|
pub public_identity_key_file: PathBuf,
|
||||||
|
|
||||||
|
/// Path to file containing private encryption key.
|
||||||
|
pub private_encryption_key_file: PathBuf,
|
||||||
|
|
||||||
|
/// Path to file containing public encryption key.
|
||||||
|
pub public_encryption_key_file: PathBuf,
|
||||||
|
|
||||||
|
/// Path to file containing shared key derived with the specified gateway that is used
|
||||||
|
/// for all communication with it.
|
||||||
|
pub gateway_shared_key_file: PathBuf,
|
||||||
|
|
||||||
|
/// Path to file containing key used for encrypting and decrypting the content of an
|
||||||
|
/// acknowledgement so that nobody besides the client knows which packet it refers to.
|
||||||
|
pub ack_key_file: PathBuf,
|
||||||
|
|
||||||
|
/// Information regarding how the client should send data to gateway.
|
||||||
|
pub gateway_endpoint: GatewayEndpointConfig,
|
||||||
|
|
||||||
|
/// Path to the database containing bandwidth credentials of this client.
|
||||||
|
pub database_path: PathBuf,
|
||||||
|
|
||||||
|
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
||||||
|
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
|
||||||
|
// if you're reading this message and we have already introduced some breaking changes, feel free
|
||||||
|
// to remove that attribute since at this point the client configs should have gotten regenerated
|
||||||
|
#[serde(default)]
|
||||||
|
pub reply_surb_database_path: PathBuf,
|
||||||
|
|
||||||
|
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
||||||
|
/// It is expected to use default value and hence .toml file should not redefine this field.
|
||||||
|
pub nym_root_directory: PathBuf,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub super_struct: PhantomData<T>,
|
||||||
|
|
||||||
|
pub packet_type: Option<PacketType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl<T: NymConfig> Default for Client<T> {
|
||||||
pub fn new_default<S: Into<String>>(id: S, version: S) -> Self {
|
fn default() -> Self {
|
||||||
let network = NymNetworkDetails::new_mainnet();
|
let network = NymNetworkDetails::new_mainnet();
|
||||||
let nyxd_urls = network
|
let nyxd_urls = network
|
||||||
.endpoints
|
.endpoints
|
||||||
@@ -256,31 +545,71 @@ impl Client {
|
|||||||
.filter_map(|validator| validator.api_url())
|
.filter_map(|validator| validator.api_url())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if nym_api_urls.is_empty() {
|
||||||
|
panic!("we do not have any default nym-api urls available!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// there must be explicit checks for whether id is not empty later
|
||||||
Client {
|
Client {
|
||||||
version: version.into(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
id: id.into(),
|
id: "".to_string(),
|
||||||
disabled_credentials_mode: true,
|
disabled_credentials_mode: true,
|
||||||
nyxd_urls,
|
nyxd_urls,
|
||||||
nym_api_urls,
|
nym_api_urls,
|
||||||
}
|
private_identity_key_file: Default::default(),
|
||||||
}
|
public_identity_key_file: Default::default(),
|
||||||
|
private_encryption_key_file: Default::default(),
|
||||||
pub fn new<S: Into<String>>(
|
public_encryption_key_file: Default::default(),
|
||||||
id: S,
|
gateway_shared_key_file: Default::default(),
|
||||||
disabled_credentials_mode: bool,
|
ack_key_file: Default::default(),
|
||||||
nyxd_urls: Vec<Url>,
|
gateway_endpoint: Default::default(),
|
||||||
nym_api_urls: Vec<Url>,
|
database_path: Default::default(),
|
||||||
) -> Self {
|
reply_surb_database_path: Default::default(),
|
||||||
Client {
|
nym_root_directory: T::default_root_directory(),
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
super_struct: Default::default(),
|
||||||
id: id.into(),
|
packet_type: Default::default(),
|
||||||
disabled_credentials_mode,
|
|
||||||
nyxd_urls,
|
|
||||||
nym_api_urls,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: NymConfig> Client<T> {
|
||||||
|
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join("private_identity.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join("public_identity.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_private_encryption_key_file(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join("private_encryption.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_public_encryption_key_file(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join("public_encryption.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_gateway_shared_key_file(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join("gateway_shared.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_ack_key_file(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join("ack_key.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_reply_surb_database_path(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join("persistent_reply_store.sqlite")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_database_path(id: &str) -> PathBuf {
|
||||||
|
T::default_data_directory(id).join(CRED_DB_FILE_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Logging {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Traffic {
|
pub struct Traffic {
|
||||||
@@ -310,8 +639,6 @@ pub struct Traffic {
|
|||||||
/// Note that its use decreases overall anonymity.
|
/// Note that its use decreases overall anonymity.
|
||||||
/// Do not set it it unless you understand the consequences of that change.
|
/// Do not set it it unless you understand the consequences of that change.
|
||||||
pub secondary_packet_size: Option<PacketSize>,
|
pub secondary_packet_size: Option<PacketSize>,
|
||||||
|
|
||||||
pub packet_type: PacketType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Traffic {
|
impl Traffic {
|
||||||
@@ -335,7 +662,6 @@ impl Default for Traffic {
|
|||||||
disable_main_poisson_packet_distribution: false,
|
disable_main_poisson_packet_distribution: false,
|
||||||
primary_packet_size: PacketSize::RegularPacket,
|
primary_packet_size: PacketSize::RegularPacket,
|
||||||
secondary_packet_size: None,
|
secondary_packet_size: None,
|
||||||
packet_type: PacketType::Mix,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::config::old_config_v1_1_20::{
|
use crate::config::{
|
||||||
AcknowledgementsV1_1_20, ClientV1_1_20, ConfigV1_1_20, CoverTrafficV1_1_20, DebugConfigV1_1_20,
|
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, Logging,
|
||||||
GatewayConnectionV1_1_20, LoggingV1_1_20, ReplySurbsV1_1_20, TopologyV1_1_20, TrafficV1_1_20,
|
ReplySurbs, Topology, Traffic, DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER,
|
||||||
DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY,
|
DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||||
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
|
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
|
||||||
DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE,
|
DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||||
DEFAULT_MAXIMUM_REPLY_SURB_AGE, DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
|
DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||||
DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
|
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
|
||||||
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||||
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||||
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
||||||
};
|
};
|
||||||
|
use nym_config::NymConfig;
|
||||||
use nym_sphinx::params::PacketSize;
|
use nym_sphinx::params::PacketSize;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -39,21 +40,21 @@ impl From<ExtendedPacketSize> for PacketSize {
|
|||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct OldConfigV1_1_13<T> {
|
pub struct OldConfigV1_1_13<T> {
|
||||||
pub client: ClientV1_1_20<T>,
|
pub client: Client<T>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub logging: OldLoggingV1_1_13,
|
logging: Logging,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub debug: OldDebugConfigV1_1_13,
|
debug: OldDebugConfigV1_1_13,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
|
impl<T: NymConfig> Default for OldConfigV1_1_13<T> {
|
||||||
#[serde(deny_unknown_fields)]
|
fn default() -> Self {
|
||||||
pub struct OldLoggingV1_1_13 {}
|
OldConfigV1_1_13 {
|
||||||
|
client: Client::<T>::default(),
|
||||||
impl From<OldLoggingV1_1_13> for LoggingV1_1_20 {
|
logging: Default::default(),
|
||||||
fn from(_value: OldLoggingV1_1_13) -> Self {
|
debug: Default::default(),
|
||||||
LoggingV1_1_20 {}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,10 +115,10 @@ pub struct OldDebugConfigV1_1_13 {
|
|||||||
pub maximum_reply_key_age: Duration,
|
pub maximum_reply_key_age: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 {
|
impl From<OldDebugConfigV1_1_13> for DebugConfig {
|
||||||
fn from(value: OldDebugConfigV1_1_13) -> Self {
|
fn from(value: OldDebugConfigV1_1_13) -> Self {
|
||||||
DebugConfigV1_1_20 {
|
DebugConfig {
|
||||||
traffic: TrafficV1_1_20 {
|
traffic: Traffic {
|
||||||
average_packet_delay: value.average_packet_delay,
|
average_packet_delay: value.average_packet_delay,
|
||||||
message_sending_average_delay: value.message_sending_average_delay,
|
message_sending_average_delay: value.message_sending_average_delay,
|
||||||
disable_main_poisson_packet_distribution: value
|
disable_main_poisson_packet_distribution: value
|
||||||
@@ -125,25 +126,25 @@ impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 {
|
|||||||
primary_packet_size: PacketSize::RegularPacket,
|
primary_packet_size: PacketSize::RegularPacket,
|
||||||
secondary_packet_size: value.use_extended_packet_size.map(Into::into),
|
secondary_packet_size: value.use_extended_packet_size.map(Into::into),
|
||||||
},
|
},
|
||||||
cover_traffic: CoverTrafficV1_1_20 {
|
cover_traffic: CoverTraffic {
|
||||||
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
|
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
|
||||||
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
|
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
|
||||||
..CoverTrafficV1_1_20::default()
|
..CoverTraffic::default()
|
||||||
},
|
},
|
||||||
gateway_connection: GatewayConnectionV1_1_20 {
|
gateway_connection: GatewayConnection {
|
||||||
gateway_response_timeout: value.gateway_response_timeout,
|
gateway_response_timeout: value.gateway_response_timeout,
|
||||||
},
|
},
|
||||||
acknowledgements: AcknowledgementsV1_1_20 {
|
acknowledgements: Acknowledgements {
|
||||||
average_ack_delay: value.average_ack_delay,
|
average_ack_delay: value.average_ack_delay,
|
||||||
ack_wait_multiplier: value.ack_wait_multiplier,
|
ack_wait_multiplier: value.ack_wait_multiplier,
|
||||||
ack_wait_addition: value.ack_wait_addition,
|
ack_wait_addition: value.ack_wait_addition,
|
||||||
},
|
},
|
||||||
topology: TopologyV1_1_20 {
|
topology: Topology {
|
||||||
topology_refresh_rate: value.topology_refresh_rate,
|
topology_refresh_rate: value.topology_refresh_rate,
|
||||||
topology_resolution_timeout: value.topology_resolution_timeout,
|
topology_resolution_timeout: value.topology_resolution_timeout,
|
||||||
disable_refreshing: false,
|
disable_refreshing: false,
|
||||||
},
|
},
|
||||||
reply_surbs: ReplySurbsV1_1_20 {
|
reply_surbs: ReplySurbs {
|
||||||
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
|
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
|
||||||
maximum_reply_surb_storage_threshold: value.maximum_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,
|
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
|
||||||
@@ -190,10 +191,10 @@ impl Default for OldDebugConfigV1_1_13 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_20<U> {
|
impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
|
||||||
fn from(value: OldConfigV1_1_13<T>) -> Self {
|
fn from(value: OldConfigV1_1_13<T>) -> Self {
|
||||||
ConfigV1_1_20 {
|
Config {
|
||||||
client: ClientV1_1_20 {
|
client: Client {
|
||||||
version: value.client.version,
|
version: value.client.version,
|
||||||
id: value.client.id,
|
id: value.client.id,
|
||||||
disabled_credentials_mode: value.client.disabled_credentials_mode,
|
disabled_credentials_mode: value.client.disabled_credentials_mode,
|
||||||
@@ -209,10 +210,10 @@ impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_20<U> {
|
|||||||
database_path: value.client.database_path,
|
database_path: value.client.database_path,
|
||||||
reply_surb_database_path: value.client.reply_surb_database_path,
|
reply_surb_database_path: value.client.reply_surb_database_path,
|
||||||
nym_root_directory: value.client.nym_root_directory,
|
nym_root_directory: value.client.nym_root_directory,
|
||||||
|
|
||||||
super_struct: PhantomData,
|
super_struct: PhantomData,
|
||||||
|
packet_type: Some(nym_sphinx::params::PacketType::Mix),
|
||||||
},
|
},
|
||||||
logging: value.logging.into(),
|
logging: value.logging,
|
||||||
debug: value.debug.into(),
|
debug: value.debug.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,339 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::config::old_config_v1_1_20_2::{
|
|
||||||
AcknowledgementsV1_1_20_2, CoverTrafficV1_1_20_2, DebugConfigV1_1_20_2,
|
|
||||||
GatewayConnectionV1_1_20_2, GatewayEndpointConfigV1_1_20_2, ReplySurbsV1_1_20_2,
|
|
||||||
TopologyV1_1_20_2, TrafficV1_1_20_2,
|
|
||||||
};
|
|
||||||
use nym_sphinx::params::{PacketSize, PacketType};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::time::Duration;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
// 'DEBUG'
|
|
||||||
pub(crate) const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
|
||||||
|
|
||||||
pub(crate) const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
|
|
||||||
pub(crate) const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
|
|
||||||
pub(crate) const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
|
|
||||||
pub(crate) const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
|
|
||||||
pub(crate) const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
|
|
||||||
pub(crate) const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
|
|
||||||
// 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
|
|
||||||
pub(crate) const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
|
||||||
|
|
||||||
pub(crate) 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
|
|
||||||
pub(crate) const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
|
|
||||||
pub(crate) 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
|
|
||||||
pub(crate) const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
|
|
||||||
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
|
|
||||||
|
|
||||||
pub(crate) const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
|
|
||||||
|
|
||||||
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration =
|
|
||||||
Duration::from_secs(10);
|
|
||||||
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration =
|
|
||||||
Duration::from_secs(5 * 60);
|
|
||||||
|
|
||||||
// 12 hours
|
|
||||||
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
|
|
||||||
|
|
||||||
// 24 hours
|
|
||||||
pub(crate) 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_20<T> {
|
|
||||||
pub client: ClientV1_1_20<T>,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub logging: LoggingV1_1_20,
|
|
||||||
#[serde(default)]
|
|
||||||
pub debug: DebugConfigV1_1_20,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
pub struct GatewayEndpointConfigV1_1_20 {
|
|
||||||
pub gateway_id: String,
|
|
||||||
pub gateway_owner: String,
|
|
||||||
pub gateway_listener: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
|
|
||||||
fn from(value: GatewayEndpointConfigV1_1_20) -> Self {
|
|
||||||
GatewayEndpointConfigV1_1_20_2 {
|
|
||||||
gateway_id: value.gateway_id,
|
|
||||||
gateway_owner: value.gateway_owner,
|
|
||||||
gateway_listener: value.gateway_listener,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
pub struct ClientV1_1_20<T> {
|
|
||||||
pub version: String,
|
|
||||||
pub id: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub disabled_credentials_mode: bool,
|
|
||||||
#[serde(alias = "validator_urls")]
|
|
||||||
pub nyxd_urls: Vec<Url>,
|
|
||||||
#[serde(alias = "validator_api_urls")]
|
|
||||||
pub nym_api_urls: Vec<Url>,
|
|
||||||
pub private_identity_key_file: PathBuf,
|
|
||||||
pub public_identity_key_file: PathBuf,
|
|
||||||
pub private_encryption_key_file: PathBuf,
|
|
||||||
pub public_encryption_key_file: PathBuf,
|
|
||||||
pub gateway_shared_key_file: PathBuf,
|
|
||||||
pub ack_key_file: PathBuf,
|
|
||||||
pub gateway_endpoint: GatewayEndpointConfigV1_1_20,
|
|
||||||
pub database_path: PathBuf,
|
|
||||||
#[serde(default)]
|
|
||||||
pub reply_surb_database_path: PathBuf,
|
|
||||||
pub nym_root_directory: PathBuf,
|
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub super_struct: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct LoggingV1_1_20 {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
pub struct TrafficV1_1_20 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub average_packet_delay: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub message_sending_average_delay: Duration,
|
|
||||||
pub disable_main_poisson_packet_distribution: bool,
|
|
||||||
pub primary_packet_size: PacketSize,
|
|
||||||
pub secondary_packet_size: Option<PacketSize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TrafficV1_1_20> for TrafficV1_1_20_2 {
|
|
||||||
fn from(value: TrafficV1_1_20) -> Self {
|
|
||||||
TrafficV1_1_20_2 {
|
|
||||||
average_packet_delay: value.average_packet_delay,
|
|
||||||
message_sending_average_delay: value.message_sending_average_delay,
|
|
||||||
disable_main_poisson_packet_distribution: value
|
|
||||||
.disable_main_poisson_packet_distribution,
|
|
||||||
primary_packet_size: value.primary_packet_size,
|
|
||||||
secondary_packet_size: value.secondary_packet_size,
|
|
||||||
packet_type: PacketType::Mix,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TrafficV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
TrafficV1_1_20 {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct CoverTrafficV1_1_20 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub loop_cover_traffic_average_delay: Duration,
|
|
||||||
pub cover_traffic_primary_size_ratio: f64,
|
|
||||||
pub disable_loop_cover_traffic_stream: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CoverTrafficV1_1_20> for CoverTrafficV1_1_20_2 {
|
|
||||||
fn from(value: CoverTrafficV1_1_20) -> Self {
|
|
||||||
CoverTrafficV1_1_20_2 {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CoverTrafficV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
CoverTrafficV1_1_20 {
|
|
||||||
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_20 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub gateway_response_timeout: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GatewayConnectionV1_1_20> for GatewayConnectionV1_1_20_2 {
|
|
||||||
fn from(value: GatewayConnectionV1_1_20) -> Self {
|
|
||||||
GatewayConnectionV1_1_20_2 {
|
|
||||||
gateway_response_timeout: value.gateway_response_timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GatewayConnectionV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
GatewayConnectionV1_1_20 {
|
|
||||||
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct AcknowledgementsV1_1_20 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub average_ack_delay: Duration,
|
|
||||||
pub ack_wait_multiplier: f64,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub ack_wait_addition: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AcknowledgementsV1_1_20> for AcknowledgementsV1_1_20_2 {
|
|
||||||
fn from(value: AcknowledgementsV1_1_20) -> Self {
|
|
||||||
AcknowledgementsV1_1_20_2 {
|
|
||||||
average_ack_delay: value.average_ack_delay,
|
|
||||||
ack_wait_multiplier: value.ack_wait_multiplier,
|
|
||||||
ack_wait_addition: value.ack_wait_addition,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AcknowledgementsV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
AcknowledgementsV1_1_20 {
|
|
||||||
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_20 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub topology_refresh_rate: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub topology_resolution_timeout: Duration,
|
|
||||||
pub disable_refreshing: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TopologyV1_1_20> for TopologyV1_1_20_2 {
|
|
||||||
fn from(value: TopologyV1_1_20) -> Self {
|
|
||||||
TopologyV1_1_20_2 {
|
|
||||||
topology_refresh_rate: value.topology_refresh_rate,
|
|
||||||
topology_resolution_timeout: value.topology_resolution_timeout,
|
|
||||||
disable_refreshing: value.disable_refreshing,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TopologyV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
TopologyV1_1_20 {
|
|
||||||
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
|
||||||
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
|
||||||
disable_refreshing: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct ReplySurbsV1_1_20 {
|
|
||||||
pub minimum_reply_surb_storage_threshold: usize,
|
|
||||||
pub maximum_reply_surb_storage_threshold: usize,
|
|
||||||
pub minimum_reply_surb_request_size: u32,
|
|
||||||
pub maximum_reply_surb_request_size: u32,
|
|
||||||
pub maximum_allowed_reply_surb_request_size: u32,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_surb_rerequest_waiting_period: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_surb_drop_waiting_period: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_surb_age: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_key_age: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ReplySurbsV1_1_20> for ReplySurbsV1_1_20_2 {
|
|
||||||
fn from(value: ReplySurbsV1_1_20) -> Self {
|
|
||||||
ReplySurbsV1_1_20_2 {
|
|
||||||
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,
|
|
||||||
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
|
|
||||||
maximum_allowed_reply_surb_request_size: value.maximum_allowed_reply_surb_request_size,
|
|
||||||
maximum_reply_surb_rerequest_waiting_period: value
|
|
||||||
.maximum_reply_surb_rerequest_waiting_period,
|
|
||||||
maximum_reply_surb_drop_waiting_period: value.maximum_reply_surb_drop_waiting_period,
|
|
||||||
maximum_reply_surb_age: value.maximum_reply_surb_age,
|
|
||||||
maximum_reply_key_age: value.maximum_reply_key_age,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ReplySurbsV1_1_20 {
|
|
||||||
fn default() -> Self {
|
|
||||||
ReplySurbsV1_1_20 {
|
|
||||||
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_20 {
|
|
||||||
pub traffic: TrafficV1_1_20,
|
|
||||||
pub cover_traffic: CoverTrafficV1_1_20,
|
|
||||||
pub gateway_connection: GatewayConnectionV1_1_20,
|
|
||||||
pub acknowledgements: AcknowledgementsV1_1_20,
|
|
||||||
pub topology: TopologyV1_1_20,
|
|
||||||
pub reply_surbs: ReplySurbsV1_1_20,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DebugConfigV1_1_20> for DebugConfigV1_1_20_2 {
|
|
||||||
fn from(value: DebugConfigV1_1_20) -> Self {
|
|
||||||
DebugConfigV1_1_20_2 {
|
|
||||||
traffic: value.traffic.into(),
|
|
||||||
cover_traffic: value.cover_traffic.into(),
|
|
||||||
gateway_connection: value.gateway_connection.into(),
|
|
||||||
acknowledgements: value.acknowledgements.into(),
|
|
||||||
topology: value.topology.into(),
|
|
||||||
reply_surbs: value.reply_surbs.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::config::{
|
|
||||||
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection,
|
|
||||||
GatewayEndpointConfig, ReplySurbs, Topology, Traffic,
|
|
||||||
};
|
|
||||||
use nym_sphinx::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);
|
|
||||||
// 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_20_2 {
|
|
||||||
pub client: ClientV1_1_20_2,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub debug: DebugConfigV1_1_20_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigV1_1_20_2> for Config {
|
|
||||||
fn from(value: ConfigV1_1_20_2) -> Self {
|
|
||||||
Config {
|
|
||||||
client: value.client.into(),
|
|
||||||
debug: value.debug.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
pub struct GatewayEndpointConfigV1_1_20_2 {
|
|
||||||
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
|
||||||
/// If initially omitted, a random gateway will be chosen from the available topology.
|
|
||||||
pub gateway_id: String,
|
|
||||||
|
|
||||||
/// Address of the gateway owner to which the client should send messages.
|
|
||||||
pub gateway_owner: String,
|
|
||||||
|
|
||||||
/// Address of the gateway listener to which all client requests should be sent.
|
|
||||||
pub gateway_listener: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GatewayEndpointConfigV1_1_20_2> for GatewayEndpointConfig {
|
|
||||||
fn from(value: GatewayEndpointConfigV1_1_20_2) -> Self {
|
|
||||||
GatewayEndpointConfig {
|
|
||||||
gateway_id: value.gateway_id,
|
|
||||||
gateway_owner: value.gateway_owner,
|
|
||||||
gateway_listener: value.gateway_listener,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
|
||||||
pub struct ClientV1_1_20_2 {
|
|
||||||
pub version: String,
|
|
||||||
|
|
||||||
pub id: String,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub disabled_credentials_mode: bool,
|
|
||||||
|
|
||||||
#[serde(alias = "validator_urls")]
|
|
||||||
pub nyxd_urls: Vec<Url>,
|
|
||||||
|
|
||||||
#[serde(alias = "validator_api_urls")]
|
|
||||||
pub nym_api_urls: Vec<Url>,
|
|
||||||
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ClientV1_1_20_2> for Client {
|
|
||||||
fn from(value: ClientV1_1_20_2) -> Self {
|
|
||||||
Client {
|
|
||||||
version: value.version,
|
|
||||||
id: value.id,
|
|
||||||
disabled_credentials_mode: value.disabled_credentials_mode,
|
|
||||||
nyxd_urls: value.nyxd_urls,
|
|
||||||
nym_api_urls: value.nym_api_urls,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
pub struct TrafficV1_1_20_2 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub average_packet_delay: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub message_sending_average_delay: Duration,
|
|
||||||
pub disable_main_poisson_packet_distribution: bool,
|
|
||||||
pub primary_packet_size: PacketSize,
|
|
||||||
pub secondary_packet_size: Option<PacketSize>,
|
|
||||||
pub packet_type: PacketType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TrafficV1_1_20_2> for Traffic {
|
|
||||||
fn from(value: TrafficV1_1_20_2) -> Self {
|
|
||||||
Traffic {
|
|
||||||
average_packet_delay: value.average_packet_delay,
|
|
||||||
message_sending_average_delay: value.message_sending_average_delay,
|
|
||||||
disable_main_poisson_packet_distribution: value
|
|
||||||
.disable_main_poisson_packet_distribution,
|
|
||||||
primary_packet_size: value.primary_packet_size,
|
|
||||||
secondary_packet_size: value.secondary_packet_size,
|
|
||||||
packet_type: PacketType::Mix,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TrafficV1_1_20_2 {
|
|
||||||
fn default() -> Self {
|
|
||||||
TrafficV1_1_20_2 {
|
|
||||||
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_20_2 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub loop_cover_traffic_average_delay: Duration,
|
|
||||||
pub cover_traffic_primary_size_ratio: f64,
|
|
||||||
pub disable_loop_cover_traffic_stream: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CoverTrafficV1_1_20_2> for CoverTraffic {
|
|
||||||
fn from(value: CoverTrafficV1_1_20_2) -> Self {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CoverTrafficV1_1_20_2 {
|
|
||||||
fn default() -> Self {
|
|
||||||
CoverTrafficV1_1_20_2 {
|
|
||||||
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_20_2 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub gateway_response_timeout: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GatewayConnectionV1_1_20_2> for GatewayConnection {
|
|
||||||
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
|
|
||||||
GatewayConnection {
|
|
||||||
gateway_response_timeout: value.gateway_response_timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GatewayConnectionV1_1_20_2 {
|
|
||||||
fn default() -> Self {
|
|
||||||
GatewayConnectionV1_1_20_2 {
|
|
||||||
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct AcknowledgementsV1_1_20_2 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub average_ack_delay: Duration,
|
|
||||||
pub ack_wait_multiplier: f64,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub ack_wait_addition: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AcknowledgementsV1_1_20_2> for Acknowledgements {
|
|
||||||
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
|
|
||||||
Acknowledgements {
|
|
||||||
average_ack_delay: value.average_ack_delay,
|
|
||||||
ack_wait_multiplier: value.ack_wait_multiplier,
|
|
||||||
ack_wait_addition: value.ack_wait_addition,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AcknowledgementsV1_1_20_2 {
|
|
||||||
fn default() -> Self {
|
|
||||||
AcknowledgementsV1_1_20_2 {
|
|
||||||
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_20_2 {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub topology_refresh_rate: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub topology_resolution_timeout: Duration,
|
|
||||||
pub disable_refreshing: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TopologyV1_1_20_2 {
|
|
||||||
fn default() -> Self {
|
|
||||||
TopologyV1_1_20_2 {
|
|
||||||
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
|
||||||
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
|
||||||
disable_refreshing: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TopologyV1_1_20_2> for Topology {
|
|
||||||
fn from(value: TopologyV1_1_20_2) -> Self {
|
|
||||||
Topology {
|
|
||||||
topology_refresh_rate: value.topology_refresh_rate,
|
|
||||||
topology_resolution_timeout: value.topology_resolution_timeout,
|
|
||||||
disable_refreshing: value.disable_refreshing,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct ReplySurbsV1_1_20_2 {
|
|
||||||
pub minimum_reply_surb_storage_threshold: usize,
|
|
||||||
pub maximum_reply_surb_storage_threshold: usize,
|
|
||||||
pub minimum_reply_surb_request_size: u32,
|
|
||||||
pub maximum_reply_surb_request_size: u32,
|
|
||||||
pub maximum_allowed_reply_surb_request_size: u32,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_surb_rerequest_waiting_period: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_surb_drop_waiting_period: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_surb_age: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub maximum_reply_key_age: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ReplySurbsV1_1_20_2 {
|
|
||||||
fn default() -> Self {
|
|
||||||
ReplySurbsV1_1_20_2 {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ReplySurbsV1_1_20_2> for ReplySurbs {
|
|
||||||
fn from(value: ReplySurbsV1_1_20_2) -> Self {
|
|
||||||
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,
|
|
||||||
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
|
|
||||||
maximum_allowed_reply_surb_request_size: value.maximum_allowed_reply_surb_request_size,
|
|
||||||
maximum_reply_surb_rerequest_waiting_period: value
|
|
||||||
.maximum_reply_surb_rerequest_waiting_period,
|
|
||||||
maximum_reply_surb_drop_waiting_period: value.maximum_reply_surb_drop_waiting_period,
|
|
||||||
maximum_reply_surb_age: value.maximum_reply_surb_age,
|
|
||||||
maximum_reply_key_age: value.maximum_reply_key_age,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
|
||||||
#[serde(default, deny_unknown_fields)]
|
|
||||||
pub struct DebugConfigV1_1_20_2 {
|
|
||||||
pub traffic: TrafficV1_1_20_2,
|
|
||||||
pub cover_traffic: CoverTrafficV1_1_20_2,
|
|
||||||
pub gateway_connection: GatewayConnectionV1_1_20_2,
|
|
||||||
pub acknowledgements: AcknowledgementsV1_1_20_2,
|
|
||||||
pub topology: TopologyV1_1_20_2,
|
|
||||||
pub reply_surbs: ReplySurbsV1_1_20_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DebugConfigV1_1_20_2> for DebugConfig {
|
|
||||||
fn from(value: DebugConfigV1_1_20_2) -> Self {
|
|
||||||
DebugConfig {
|
|
||||||
traffic: value.traffic.into(),
|
|
||||||
cover_traffic: value.cover_traffic.into(),
|
|
||||||
gateway_connection: value.gateway_connection.into(),
|
|
||||||
acknowledgements: value.acknowledgements.into(),
|
|
||||||
topology: value.topology.into(),
|
|
||||||
reply_surbs: value.reply_surbs.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use nym_config::NymConfig;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ClientKeyPathfinder {
|
||||||
|
pub identity_private_key: PathBuf,
|
||||||
|
pub identity_public_key: PathBuf,
|
||||||
|
pub encryption_private_key: PathBuf,
|
||||||
|
pub encryption_public_key: PathBuf,
|
||||||
|
pub gateway_shared_key: PathBuf,
|
||||||
|
pub ack_key: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientKeyPathfinder {
|
||||||
|
pub fn new(id: String) -> Self {
|
||||||
|
let os_config_dir = dirs::config_dir().expect("no config directory known for this OS"); // grabs the OS default config dir
|
||||||
|
let config_dir = os_config_dir.join("nym").join("clients").join(id);
|
||||||
|
ClientKeyPathfinder {
|
||||||
|
identity_private_key: config_dir.join("private_identity.pem"),
|
||||||
|
identity_public_key: config_dir.join("public_identity.pem"),
|
||||||
|
encryption_private_key: config_dir.join("private_encryption.pem"),
|
||||||
|
encryption_public_key: config_dir.join("public_encryption.pem"),
|
||||||
|
gateway_shared_key: config_dir.join("gateway_shared.pem"),
|
||||||
|
ack_key: config_dir.join("ack_key.pem"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_config<T: NymConfig>(config: &Config<T>) -> Self {
|
||||||
|
ClientKeyPathfinder {
|
||||||
|
identity_private_key: config.get_private_identity_key_file(),
|
||||||
|
identity_public_key: config.get_public_identity_key_file(),
|
||||||
|
encryption_private_key: config.get_private_encryption_key_file(),
|
||||||
|
encryption_public_key: config.get_public_encryption_key_file(),
|
||||||
|
gateway_shared_key: config.get_gateway_shared_key_file(),
|
||||||
|
ack_key: config.get_ack_key_file(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any_file_exists(&self) -> bool {
|
||||||
|
matches!(self.identity_public_key.try_exists(), Ok(true))
|
||||||
|
|| matches!(self.identity_private_key.try_exists(), Ok(true))
|
||||||
|
|| matches!(self.encryption_public_key.try_exists(), Ok(true))
|
||||||
|
|| matches!(self.encryption_private_key.try_exists(), Ok(true))
|
||||||
|
|| matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||||
|
|| matches!(self.ack_key.try_exists(), Ok(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any_file_exists_and_return(&self) -> Option<PathBuf> {
|
||||||
|
file_exists(&self.identity_public_key)
|
||||||
|
.or_else(|| file_exists(&self.identity_private_key))
|
||||||
|
.or_else(|| file_exists(&self.encryption_public_key))
|
||||||
|
.or_else(|| file_exists(&self.encryption_private_key))
|
||||||
|
.or_else(|| file_exists(&self.gateway_shared_key))
|
||||||
|
.or_else(|| file_exists(&self.ack_key))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gateway_key_file_exists(&self) -> bool {
|
||||||
|
matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn private_identity_key(&self) -> &Path {
|
||||||
|
&self.identity_private_key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn public_identity_key(&self) -> &Path {
|
||||||
|
&self.identity_public_key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn private_encryption_key(&self) -> &Path {
|
||||||
|
&self.encryption_private_key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn public_encryption_key(&self) -> &Path {
|
||||||
|
&self.encryption_public_key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gateway_shared_key(&self) -> &Path {
|
||||||
|
&self.gateway_shared_key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ack_key(&self) -> &Path {
|
||||||
|
&self.ack_key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_exists(path: &Path) -> Option<PathBuf> {
|
||||||
|
if matches!(path.try_exists(), Ok(true)) {
|
||||||
|
return Some(path.to_path_buf());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
pub mod key_pathfinder;
|
||||||
@@ -6,7 +6,6 @@ use nym_gateway_client::error::GatewayClientError;
|
|||||||
use nym_topology::gateway::GatewayConversionError;
|
use nym_topology::gateway::GatewayConversionError;
|
||||||
use nym_topology::NymTopologyError;
|
use nym_topology::NymTopologyError;
|
||||||
use nym_validator_client::ValidatorClientError;
|
use nym_validator_client::ValidatorClientError;
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum ClientCoreError {
|
pub enum ClientCoreError {
|
||||||
@@ -42,23 +41,13 @@ pub enum ClientCoreError {
|
|||||||
|
|
||||||
#[error("experienced a failure with our reply surb persistent storage: {source}")]
|
#[error("experienced a failure with our reply surb persistent storage: {source}")]
|
||||||
SurbStorageError {
|
SurbStorageError {
|
||||||
source: Box<dyn Error + Send + Sync>,
|
source: Box<dyn std::error::Error + Send + Sync>,
|
||||||
},
|
|
||||||
|
|
||||||
#[error("experienced a failure with our cryptographic keys persistent storage: {source}")]
|
|
||||||
KeyStoreError {
|
|
||||||
source: Box<dyn Error + Send + Sync>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("experienced a failure with our gateway details storage: {source}")]
|
|
||||||
GatewayDetailsStoreError {
|
|
||||||
source: Box<dyn Error + Send + Sync>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("The gateway id is invalid - {0}")]
|
#[error("The gateway id is invalid - {0}")]
|
||||||
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
||||||
|
|
||||||
#[error("The identity of the gateway is unknown - did you run init?")]
|
#[error("The identity of the gateway is unknwown - did you run init?")]
|
||||||
GatewayIdUnknown,
|
GatewayIdUnknown,
|
||||||
|
|
||||||
#[error("The owner of the gateway is unknown - did you run init?")]
|
#[error("The owner of the gateway is unknown - did you run init?")]
|
||||||
@@ -97,25 +86,6 @@ pub enum ClientCoreError {
|
|||||||
|
|
||||||
#[error("Unexpected exit")]
|
#[error("Unexpected exit")]
|
||||||
UnexpectedExit,
|
UnexpectedExit,
|
||||||
|
|
||||||
#[error(
|
|
||||||
"This operation would have resulted in clients keys being overwritten without permission"
|
|
||||||
)]
|
|
||||||
ForbiddenKeyOverwrite,
|
|
||||||
|
|
||||||
#[error("gateway details are unavailable")]
|
|
||||||
UnavailableGatewayDetails {
|
|
||||||
source: Box<dyn Error + Send + Sync>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("gateway shared key is unavailable whilst we have full node information")]
|
|
||||||
UnavailableSharedKey,
|
|
||||||
|
|
||||||
#[error("attempted to obtain fresh gateway details whilst already knowing about one")]
|
|
||||||
UnexpectedGatewayDetails,
|
|
||||||
|
|
||||||
#[error("the provided gateway details (for gateway {gateway_id}) do not correspond to the shared keys")]
|
|
||||||
MismatchedGatewayDetails { gateway_id: String },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set of messages that the client can send to listeners via the task manager
|
/// Set of messages that the client can send to listeners via the task manager
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::config::GatewayEndpointConfig;
|
use crate::{
|
||||||
use crate::error::ClientCoreError;
|
client::key_manager::KeyManager,
|
||||||
|
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
|
||||||
|
error::ClientCoreError,
|
||||||
|
};
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use log::{debug, info, trace, warn};
|
use log::{debug, info, trace, warn};
|
||||||
|
use nym_config::NymConfig;
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_gateway_client::GatewayClient;
|
use nym_gateway_client::GatewayClient;
|
||||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||||
use nym_topology::{filter::VersionFilterable, gateway};
|
use nym_topology::{filter::VersionFilterable, gateway};
|
||||||
use rand::{seq::SliceRandom, Rng};
|
use rand::{seq::SliceRandom, thread_rng, Rng};
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
use tungstenite::Message;
|
use tungstenite::Message;
|
||||||
@@ -25,8 +29,10 @@ use tokio::time::Instant;
|
|||||||
use tokio_tungstenite::connect_async;
|
use tokio_tungstenite::connect_async;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
||||||
|
use nym_credential_storage::storage::Storage;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use nym_bandwidth_controller::wasm_mockups::DirectSigningNyxdClient;
|
use nym_bandwidth_controller::wasm_mockups::DirectSigningNyxdClient;
|
||||||
@@ -44,27 +50,27 @@ const MEASUREMENTS: usize = 3;
|
|||||||
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
|
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||||
const PING_TIMEOUT: Duration = Duration::from_millis(1000);
|
const PING_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||||
|
|
||||||
struct GatewayWithLatency<'a> {
|
struct GatewayWithLatency {
|
||||||
gateway: &'a gateway::Node,
|
gateway: gateway::Node,
|
||||||
latency: Duration,
|
latency: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GatewayWithLatency<'a> {
|
impl GatewayWithLatency {
|
||||||
fn new(gateway: &'a gateway::Node, latency: Duration) -> Self {
|
fn new(gateway: gateway::Node, latency: Duration) -> Self {
|
||||||
GatewayWithLatency { gateway, latency }
|
GatewayWithLatency { gateway, latency }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn current_gateways<R: Rng>(
|
async fn current_gateways<R: Rng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
nym_apis: &[Url],
|
nym_apis: Vec<Url>,
|
||||||
) -> Result<Vec<gateway::Node>, ClientCoreError> {
|
) -> Result<Vec<gateway::Node>, ClientCoreError> {
|
||||||
let nym_api = nym_apis
|
let nym_api = nym_apis
|
||||||
.choose(rng)
|
.choose(rng)
|
||||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||||
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
|
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
|
||||||
|
|
||||||
log::trace!("Fetching list of gateways from: {nym_api}");
|
log::trace!("Fetching list of gateways from: {}", nym_api);
|
||||||
|
|
||||||
let gateways = client.get_cached_gateways().await?;
|
let gateways = client.get_cached_gateways().await?;
|
||||||
let valid_gateways = gateways
|
let valid_gateways = gateways
|
||||||
@@ -91,7 +97,7 @@ async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
|||||||
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
|
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
|
async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
|
||||||
let addr = gateway.clients_address();
|
let addr = gateway.clients_address();
|
||||||
trace!(
|
trace!(
|
||||||
"establishing connection to {} ({addr})...",
|
"establishing connection to {} ({addr})...",
|
||||||
@@ -154,9 +160,9 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
|
|||||||
Ok(GatewayWithLatency::new(gateway, avg))
|
Ok(GatewayWithLatency::new(gateway, avg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn choose_gateway_by_latency<R: Rng>(
|
async fn choose_gateway_by_latency<R: Rng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
gateways: &[gateway::Node],
|
gateways: Vec<gateway::Node>,
|
||||||
) -> Result<gateway::Node, ClientCoreError> {
|
) -> Result<gateway::Node, ClientCoreError> {
|
||||||
info!("choosing gateway by latency...");
|
info!("choosing gateway by latency...");
|
||||||
|
|
||||||
@@ -187,9 +193,9 @@ pub(super) async fn choose_gateway_by_latency<R: Rng>(
|
|||||||
Ok(chosen.gateway.clone())
|
Ok(chosen.gateway.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn uniformly_random_gateway<R: Rng>(
|
fn uniformly_random_gateway<R: Rng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
gateways: &[gateway::Node],
|
gateways: Vec<gateway::Node>,
|
||||||
) -> Result<gateway::Node, ClientCoreError> {
|
) -> Result<gateway::Node, ClientCoreError> {
|
||||||
gateways
|
gateways
|
||||||
.choose(rng)
|
.choose(rng)
|
||||||
@@ -197,14 +203,35 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
|
|||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn register_with_gateway(
|
pub(super) async fn query_gateway_details(
|
||||||
gateway: &GatewayEndpointConfig,
|
validator_servers: Vec<Url>,
|
||||||
|
chosen_gateway_id: Option<identity::PublicKey>,
|
||||||
|
by_latency: bool,
|
||||||
|
) -> Result<gateway::Node, ClientCoreError> {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let gateways = current_gateways(&mut rng, validator_servers).await?;
|
||||||
|
|
||||||
|
// if we set an explicit gateway, use that one and nothing else
|
||||||
|
if let Some(explicitly_chosen) = chosen_gateway_id {
|
||||||
|
gateways
|
||||||
|
.into_iter()
|
||||||
|
.find(|gateway| gateway.identity_key == explicitly_chosen)
|
||||||
|
.ok_or_else(|| ClientCoreError::NoGatewayWithId(explicitly_chosen.to_string()))
|
||||||
|
} else if by_latency {
|
||||||
|
choose_gateway_by_latency(&mut rng, gateways).await
|
||||||
|
} else {
|
||||||
|
uniformly_random_gateway(&mut rng, gateways)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn register_with_gateway<St: Storage>(
|
||||||
|
gateway: &gateway::Node,
|
||||||
our_identity: Arc<identity::KeyPair>,
|
our_identity: Arc<identity::KeyPair>,
|
||||||
) -> Result<Arc<SharedKeys>, ClientCoreError> {
|
) -> Result<Arc<SharedKeys>, ClientCoreError> {
|
||||||
let timeout = Duration::from_millis(1500);
|
let timeout = Duration::from_millis(1500);
|
||||||
let mut gateway_client: GatewayClient<DirectSigningNyxdClient, _> = GatewayClient::new_init(
|
let mut gateway_client: GatewayClient<DirectSigningNyxdClient, St> = GatewayClient::new_init(
|
||||||
gateway.gateway_listener.clone(),
|
gateway.clients_address(),
|
||||||
gateway.try_get_gateway_identity_key()?,
|
gateway.identity_key,
|
||||||
our_identity.clone(),
|
our_identity.clone(),
|
||||||
timeout,
|
timeout,
|
||||||
);
|
);
|
||||||
@@ -218,3 +245,16 @@ pub(super) async fn register_with_gateway(
|
|||||||
.tap_err(|_| log::warn!("Failed to register with the gateway!"))?;
|
.tap_err(|_| log::warn!("Failed to register with the gateway!"))?;
|
||||||
Ok(shared_keys)
|
Ok(shared_keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn store_keys<T>(
|
||||||
|
key_manager: &KeyManager,
|
||||||
|
config: &Config<T>,
|
||||||
|
) -> Result<(), ClientCoreError>
|
||||||
|
where
|
||||||
|
T: NymConfig,
|
||||||
|
{
|
||||||
|
let pathfinder = ClientKeyPathfinder::new_from_config(config);
|
||||||
|
Ok(key_manager
|
||||||
|
.store_keys(&pathfinder)
|
||||||
|
.tap_err(|err| log::error!("Failed to generate keys: {err}"))?)
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user