Compare commits

..

1 Commits

Author SHA1 Message Date
mx 7e7200a7c8 added note that operators can decrease self bond via wallet 2023-04-24 15:07:41 +02:00
806 changed files with 16778 additions and 55180 deletions
+22 -17
View File
@@ -11,25 +11,30 @@
# In each subsection folders are ordered first by depth, then alphabetically. # In each subsection folders are ordered first by depth, then alphabetically.
# This should make it easy to add new rules without breaking existing ones. # This should make it easy to add new rules without breaking existing ones.
# contracts # Something weird not covered by anything else
/contracts/mixnet @durch @jstuczyn * @futurechimp @mmsinclair
/contracts/vesting @durch @jstuczyn
/contracts/service-provider-directory @octol
# crypto code # Rust rules:
/common/crypto/ @jstuczyn *.rs @durch @futurechimp @jstuczyn @neacsu @octol
/common/nymcoconut/ @jstuczyn Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol
/common/dkg/ @jstuczyn
/common/nymsphinx/ @jstuczyn
# rust sdk # JS rules:
/sdk/rust/ @octol *.js @mmsinclair @fmtabbara
*.ts @mmsinclair @fmtabbara
*.tsx @mmsinclair @fmtabbara
*.jsx @mmsinclair @fmtabbara
# nym-connect (rust) # Something looking like possible documentation rules:
/nym-connect/desktop/src-tauri/ @octol *.md @mfahampshire
# nym-wallet (rust) # our docker scripts
/nym-wallet/src-tauri/ @octol /docker/ @neacsu
# documentation # if there are any changes in the core crypto, I feel like Ania should take a look:
/documentation @mfahampshire /common/crypto/ @aniampio
/common/nymsphinx/ @aniampio
# Explorer and wallet should probably get looked by the product team
/explorer/ @nymtech/product
/nym-wallet/ @nymtech/product
/wallet-web/ @nymtech/product
-14
View File
@@ -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:
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
- name: install npm - name: install npm
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Matrix - Node Install - name: Matrix - Node Install
run: npm install run: npm install
working-directory: .github/workflows/support-files working-directory: .github/workflows/support-files
@@ -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
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
- name: Build - name: Build
-4
View File
@@ -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:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: "16"
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: "16"
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: "16"
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: "16"
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
+138
View File
@@ -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 -1
View File
@@ -20,7 +20,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install Yarn - name: Install Yarn
run: npm install -g yarn run: npm install -g yarn
- run: yarn - run: yarn
@@ -0,0 +1,24 @@
name: Linting for Network Explorer (eslint/prettier)
on:
pull_request:
paths:
- 'explorer/**'
defaults:
run:
working-directory: explorer
jobs:
build:
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Setup yarn
run: npm install -g yarn
- name: Run ESLint
# GitHub should automatically annotate the PR
run: yarn && yarn lint
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
continue-on-error: true continue-on-error: true
+1 -1
View File
@@ -152,7 +152,7 @@ jobs:
uses: actions/setup-node@v3 uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
with: with:
node-version: 18 node-version: 16
- name: Matrix - Node Install - name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install run: npm install
@@ -6,7 +6,7 @@
}, },
{ {
"os":"windows10", "os":"windows-latest",
"rust":"stable", "rust":"stable",
"runOnEvent":"schedule" "runOnEvent":"schedule"
}, },
@@ -22,7 +22,7 @@
"runOnEvent":"schedule" "runOnEvent":"schedule"
}, },
{ {
"os":"windows10", "os":"windows-latest",
"rust":"beta", "rust":"beta",
"runOnEvent":"schedule" "runOnEvent":"schedule"
}, },
@@ -38,7 +38,7 @@
"runOnEvent":"schedule" "runOnEvent":"schedule"
}, },
{ {
"os":"windows10", "os":"windows-latest",
"rust":"nightly", "rust":"nightly",
"runOnEvent":"schedule" "runOnEvent":"schedule"
}, },
+1 -1
View File
@@ -167,7 +167,7 @@ jobs:
uses: actions/setup-node@v3 uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
with: with:
node-version: 18 node-version: 16
- name: Matrix - Node Install - name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install run: npm install
+1 -1
View File
@@ -167,7 +167,7 @@ jobs:
uses: actions/setup-node@v3 uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
with: with:
node-version: 18 node-version: 16
- name: Matrix - Node Install - name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install run: npm install
@@ -20,10 +20,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Node - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
@@ -51,15 +51,11 @@ jobs:
security list-keychain -d user -s $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH
- name: Create env file - name: Create env file
uses: timheuer/base64-to-file@v1.2 uses: timheuer/base64-to-file@v1.1
with: with:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies and build it - name: Install app dependencies and build it
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -77,7 +73,7 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: nym-connect_1.0.0_x64.dmg name: nym-connect_1.0.0_x64.dmg
path: nym-connect/desktop/target/release/bundle/dmg/nym-connect_1*_x64.dmg path: nym-connect/desktop/target/release/bundle/dmg/nym-connect_1.0.0_x64.dmg
retention-days: 30 retention-days: 30
- name: Clean up keychain - name: Clean up keychain
@@ -26,25 +26,19 @@ jobs:
sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev
continue-on-error: true continue-on-error: true
- name: Node - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies - name: Install app dependencies
run: yarn run: yarn
- name: Create env file - name: Create env file
uses: timheuer/base64-to-file@v1.2 uses: timheuer/base64-to-file@v1.1
with: with:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
@@ -59,7 +53,7 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: nym-connect.AppImage.tar.gz name: nym-connect.AppImage.tar.gz
path: nym-connect/desktop/target/release/bundle/appimage/nym-connect_1*_amd64.AppImage path: nym-connect/desktop/target/release/bundle/appimage/nym-connect_1.0.0_amd64.AppImage
retention-days: 30 retention-days: 30
- name: Upload to release based on tag name - name: Upload to release based on tag name
@@ -39,10 +39,10 @@ jobs:
Remove-Item -path certificate -include tempCert.txt Remove-Item -path certificate -include tempCert.txt
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText) Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
- name: Node - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -50,21 +50,15 @@ jobs:
toolchain: stable toolchain: stable
- name: Create env file - name: Create env file
uses: timheuer/base64-to-file@v1.2 uses: timheuer/base64-to-file@v1.1
with: with:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies - name: Install app dependencies
shell: bash
run: yarn --network-timeout 100000 run: yarn --network-timeout 100000
- name: Build and sign it - name: Build and sign it
shell: bash
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }} ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
@@ -78,7 +72,7 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: nym-connect_1.0.0_x64_en-US.msi name: nym-connect_1.0.0_x64_en-US.msi
path: nym-connect/desktop/target/release/bundle/msi/nym-connect_1*_x64_en-US.msi path: nym-connect/desktop/target/release/bundle/msi/nym-connect_1.0.0_x64_en-US.msi
retention-days: 30 retention-days: 30
- name: Upload to release based on tag name - name: Upload to release based on tag name
@@ -20,10 +20,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Node - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
@@ -51,15 +51,11 @@ jobs:
security list-keychain -d user -s $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH
- name: Create env file - name: Create env file
uses: timheuer/base64-to-file@v1.2 uses: timheuer/base64-to-file@v1.1
with: with:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies and build it - name: Install app dependencies and build it
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1,6 +1,5 @@
name: Publish Nym Wallet (Ubuntu) name: Publish Nym Wallet (Ubuntu)
on: on:
workflow_dispatch:
release: release:
types: [created] types: [created]
@@ -26,24 +25,19 @@ jobs:
sudo apt-get install -y webkit2gtk-4.0 sudo apt-get install -y webkit2gtk-4.0
continue-on-error: true continue-on-error: true
- name: Node - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies - name: Install app dependencies
run: yarn run: yarn
- name: Create env file - name: Create env file
uses: timheuer/base64-to-file@v1.2 uses: timheuer/base64-to-file@v1.1
with: with:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
@@ -53,17 +47,8 @@ jobs:
env: env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-wallet_1.0.0_amd64.AppImage.tar.gz
path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz
retention-days: 30
- name: Upload to release based on tag name - name: Upload to release based on tag name
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with: with:
files: | files: |
nym-wallet/target/release/bundle/appimage/*.AppImage nym-wallet/target/release/bundle/appimage/*.AppImage
@@ -39,10 +39,10 @@ jobs:
Remove-Item -path certificate -include tempCert.txt Remove-Item -path certificate -include tempCert.txt
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText) Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
- name: Node - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -50,21 +50,15 @@ jobs:
toolchain: stable toolchain: stable
- name: Create env file - name: Create env file
uses: timheuer/base64-to-file@v1.2 uses: timheuer/base64-to-file@v1.1
with: with:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies - name: Install app dependencies
shell: bash
run: yarn --network-timeout 100000 run: yarn --network-timeout 100000
- name: Build and sign it - name: Build and sign it
shell: bash
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }} ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
- name: Build dependencies - name: Build dependencies
+2 -2
View File
@@ -34,10 +34,10 @@ jobs:
profile: minimal profile: minimal
toolchain: stable toolchain: stable
- name: Node - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Install yarn for building application - name: Install yarn for building application
run: yarn install run: yarn install
-111
View File
@@ -1,111 +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 }}
SENTRY_AUTH_TOKEN: ${{ secrets.NYMS5_ANDROID_SENTRY_AUTH_TOKEN }}
# build for arm64 and x86_64
run: |
echo "auth.token=$SENTRY_AUTH_TOKEN" | tee -a sentry.properties
./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
-154
View File
@@ -1,154 +0,0 @@
name: Push release data
env:
strapi_download_url: 'https://strapi.feat-nym-update-nym-web.websites.dev.nymte.ch/api/downloaders'
strapi_updater_url: 'https://strapi.feat-nym-update-nym-web.websites.dev.nymte.ch/api/updaters'
on:
workflow_call:
inputs: &wf-inputs
release_tag:
required: true
description: Release tag
type: string
release_id:
required: true
description: Release ID
type: string
release_date:
required: true
description: Release date
type: string
download_base_url:
required: true
description: Download base URL
type: string
changelog_url:
required: true
description: Changelog URL
type: string
archive_url:
required: false
description: Binary archive URL
type: string
sig_url:
required: false
description: Archive signature URL
type: string
version:
required: true
description: Release version (semver)
type: string
filename:
required: true
description: Binary file name
type: string
file_hash:
required: true
description: Binary hash (sha256)
type: string
name:
required: true
description: Name
type: string
category:
required: true
description: Category
type: string
platform:
required: false
description: Platform
type: string
workflow_dispatch:
inputs:
<<: *wf-inputs
category:
required: true
description: Category
default: 'wallet'
type: choice
options:
- wallet
- connect
- binaries
platform:
required: false
description: Platform
default: 'Ubuntu'
type: choice
options:
- Ubuntu
- Windows
- MacOS
jobs:
push-download-data:
name: Push download data to Strapi
runs-on: custom-runner-linux
steps:
- id: get_sig
name: Get sig
if: ${{ inputs.sig_url != null }}
run: |
output=$(curl -LsSf ${{ inputs.sig_url }})
echo "sig=$output" >> "$GITHUB_OUTPUT"
- name: Push download data
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.strapi_download_url }}
method: 'POST'
# bearerToken: ${{ secrets.STRAPI_AUTH_TOKEN }} TODO
customHeaders: '{"Content-Type": "application/json"}'
data: |
{
"data": {
"releaseId": "${{ inputs.release_id }}",
"releaseDate": "${{ inputs.release_date }}",
"downloadBaseUrl": "${{ inputs.download_base_url }}",
"changelogUrl": "${{ inputs.changelog_url }}",
"version": "${{ inputs.version }}",
"filename": "${{ inputs.filename }}",
"name": "${{ inputs.name }}",
"category": "${{ inputs.category }}",
"platform": "${{ inputs.platform }}",
"sha256": "${{ inputs.file_hash }}",
"sig": "${{ steps.get_sig.outputs.sig }}"
}
}
push-update-data:
name: Push update data to Strapi
runs-on: ${{ matrix.platform }}
# only push update data for tauri apps (desktop wallet and NC)
if: ${{ inputs.category == 'wallet' || inputs.category == 'connect' }}
steps:
- id: get_sig
name: Get sig
if: ${{ inputs.sig_url != null }}
run: |
output=$(curl -LsSf ${{ inputs.sig_url }})
echo "sig=$output" >> "$GITHUB_OUTPUT"
- name: Push update data
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.strapi_updater_url }}
method: 'POST'
# bearerToken: ${{ secrets.STRAPI_AUTH_TOKEN }} TODO
customHeaders: '{"Content-Type": "application/json"}'
data: |
{
"data": {
"releaseId": "${{ inputs.release_id }}",
"releaseDate": "${{ inputs.release_date }}",
"downloadUrl": "${{ inputs.archive_url }}",
"changelog": "See ${{ inputs.changelog_url }} for the changelog",
"version": "${{ inputs.version }}",
"filename": "${{ inputs.filename }}",
"category": "${{ inputs.category }}",
"platform": "${{ inputs.platform }}",
"sha256": "${{ inputs.file_hash }}",
"sig": "${{ steps.get_sig.outputs.sig }}"
}
}
-35
View File
@@ -1,35 +0,0 @@
name: Publish SDK to NPM
on:
workflow_dispatch:
jobs:
publish:
runs-on: [custom-runner-linux]
steps:
- uses: actions/checkout@v2
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
- name: Setup yarn
run: npm install -g yarn
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install dependencies
run: yarn
- name: Build and publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
working-directory: ./sdk/typescript/packages/sdk
run: scripts/publish.sh
+1 -1
View File
@@ -1 +1 @@
18 16
+1 -3
View File
@@ -11,7 +11,6 @@ on:
- 'nym-connect/mobile/package.json' - 'nym-connect/mobile/package.json'
- 'nym-wallet/src/**' - 'nym-wallet/src/**'
- 'nym-wallet/package.json' - 'nym-wallet/package.json'
- 'explorer/**'
pull_request: pull_request:
paths: paths:
- 'ts-packages/**' - 'ts-packages/**'
@@ -22,7 +21,6 @@ on:
- 'nym-connect/mobile/package.json' - 'nym-connect/mobile/package.json'
- 'nym-wallet/src/**' - 'nym-wallet/src/**'
- 'nym-wallet/package.json' - 'nym-wallet/package.json'
- 'explorer/**'
jobs: jobs:
build: build:
@@ -35,7 +33,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 16
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
- name: Install - name: Install
-79
View File
@@ -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/"
-2
View File
@@ -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
@@ -43,4 +42,3 @@ envs/qwerty.env
.parcel-cache .parcel-cache
**/.DS_Store **/.DS_Store
cpu-cycles/libcpucycles/build cpu-cycles/libcpucycles/build
foxyfox.env
+7 -137
View File
@@ -4,144 +4,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [v1.1.23] (2023-07-04) - nym-network-statistics properly handles signals ([#3209])
- add socks5 support for Rust SDK ([#3226], [#3255])
- add coconut bandwidth credential support for Rust SDK ([#3273])
- nym-cli: add client identity key signing support ([#3576]) [#3209]: https://github.com/nymtech/nym/issues/3209
- Feature/node tester package ([#3634]) [#3226]: https://github.com/nymtech/nym/pull/3226
- Add medium toggle to socks5 client ([#3615]) [#3255]: https://github.com/nymtech/nym/pull/3255
- Don't fully turn off background task when cover traffic is disabled ([#3596]) [#3273]: https://github.com/nymtech/nym/pull/3273
[#3576]: https://github.com/nymtech/nym/issues/3576
[#3634]: https://github.com/nymtech/nym/pull/3634
[#3615]: https://github.com/nymtech/nym/pull/3615
[#3596]: https://github.com/nymtech/nym/pull/3596
## [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)
- Explorer - Fix sorting function on Stake Saturation. It is currently working per page and not globally ([#3320])
- Poisson process gets stuck at too slow rate. Rework to more aggressively up-regulate ([#3309])
- decrease the logging level of warnings associated with clients dropping packets due to gateway being overloaded (I'd say reduce it to debug/trace) - there are few sources of those, e.g. in real and cover traffic streams ([#3299])
- Make the buffer size in `AvailableReader` depend on packet sizes the client is using + introduce read timeouts ([#3213])
- Rust SDK - Support coconut, credential storage etc ([#2755])
- version bump for next release ([#3349])
- added coconut credential generation example ([#3339])
- update mix-node setup docs with node description ([#3325])
- exposed missing gateway commands in nym-cli ([#3324])
- make sure to clear inner 'ack_map' in 'GatewaysReader' ([#3300])
[#3320]: https://github.com/nymtech/nym/issues/3320
[#3309]: https://github.com/nymtech/nym/issues/3309
[#3299]: https://github.com/nymtech/nym/issues/3299
[#3213]: https://github.com/nymtech/nym/issues/3213
[#2755]: https://github.com/nymtech/nym/issues/2755
[#3349]: https://github.com/nymtech/nym/pull/3349
[#3339]: https://github.com/nymtech/nym/pull/3339
[#3325]: https://github.com/nymtech/nym/pull/3325
[#3324]: https://github.com/nymtech/nym/pull/3324
[#3300]: https://github.com/nymtech/nym/pull/3300
## [v1.1.15] (2023-04-18) ## [v1.1.15] (2023-04-18)
Generated
+613 -1554
View File
File diff suppressed because it is too large Load Diff
+1 -12
View File
@@ -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",
@@ -49,7 +48,6 @@ members = [
"common/ledger", "common/ledger",
"common/mixnode-common", "common/mixnode-common",
"common/network-defaults", "common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue", "common/nonexhaustive-delayqueue",
"common/nymcoconut", "common/nymcoconut",
"common/nymsphinx", "common/nymsphinx",
@@ -61,14 +59,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 +74,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 +82,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 +107,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 +114,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 +131,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"
+2 -8
View File
@@ -13,10 +13,6 @@ happy: fmt clippy-happy test
# on all workspaces. # on all workspaces.
build-release: build-release-main wasm build-release: build-release-main wasm
# Deprecated
# For backwards compatibility
clippy-all: clippy
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Define targets for a given workspace # Define targets for a given workspace
# $(1): name # $(1): name
@@ -56,11 +52,11 @@ fmt-$(1):
cargo fmt --manifest-path $(2)/Cargo.toml --all cargo fmt --manifest-path $(2)/Cargo.toml --all
clippy-happy: clippy-happy-$(1) clippy-happy: clippy-happy-$(1)
clippy: clippy-$(1) clippy-examples-$(1) clippy-all: clippy-$(1) clippy-examples-$(1)
check: check-$(1) check: check-$(1)
cargo-test: test-$(1) cargo-test: test-$(1)
cargo-test-expensive: test-expensive-$(1) cargo-test-expensive: test-expensive-$(1)
build: build-$(1) build-examples-$(1) build: build-$(1) build-$(1)-examples
build-release-all: build-release-$(1) build-release-all: build-release-$(1)
fmt: fmt-$(1) fmt: fmt-$(1)
@@ -99,7 +95,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 +105,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
+6 -9
View File
@@ -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/pre-built-binaries.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">
@@ -84,3 +80,4 @@ where `s'` is stake `s` scaled over total token circulating supply.
### Licensing and copyright information ### Licensing and copyright information
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files. This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
-2
View File
@@ -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" }
+6 -8
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.23" version = "1.1.15"
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"
+101 -92
View File
@@ -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),
}
}
}
+36 -22
View File
@@ -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 }}'
# Path to the database containing bandwidth credentials ##### advanced configuration options #####
credentials_database = '{{ storage_paths.credentials_database }}'
# Absolute path to the home Nym Clients directory.
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 }}'
"#
"#; }
+101 -59
View File
@@ -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,85 @@ 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_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 +94,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 +119,9 @@ impl SocketClient {
self_address, self_address,
shared_lane_queue_lengths, shared_lane_queue_lengths,
reply_controller_sender, reply_controller_sender,
Some(packet_type),
); );
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 +134,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 +170,40 @@ 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(self) -> Result<DirectClient, 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() {
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();
@@ -173,7 +224,6 @@ impl SocketClient {
reconstructed_receiver, reconstructed_receiver,
address, address,
shutdown_notifier: started_client.task_manager, shutdown_notifier: started_client.task_manager,
packet_type,
}) })
} }
} }
@@ -187,7 +237,6 @@ 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,
} }
impl DirectClient { impl DirectClient {
@@ -208,7 +257,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.client_input self.client_input
.input_sender .input_sender
@@ -227,13 +276,7 @@ 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 = InputMessage::new_anonymous(recipient, message, reply_surbs, lane);
recipient,
message,
reply_surbs,
lane,
Some(self.packet_type),
);
self.client_input self.client_input
.input_sender .input_sender
@@ -247,8 +290,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);
InputMessage::new_reply(recipient_tag, message, lane, Some(self.packet_type));
self.client_input self.client_input
.input_sender .input_sender
+36 -49
View File
@@ -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");
}
+13 -133
View File
@@ -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)]
+29 -7
View File
@@ -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");
+79 -11
View File
@@ -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(
mut config: Config,
_matches: &Upgrade,
config_version: &Version,
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)
};
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); let config_version = parse_config_version(&config);
if &config_version == package_version { if &config_version == package_version {
println!("You're using the most recent version!"); println!("You're using the most recent version!");
return; return;
} }
unimplemented_upgrade(package_version, &config_version) 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)
} }
+3 -10
View File
@@ -14,7 +14,6 @@ use nym_client_core::client::{
use nym_client_websocket_requests::{requests::ClientRequest, responses::ServerResponse}; use nym_client_websocket_requests::{requests::ClientRequest, responses::ServerResponse};
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_sphinx::params::PacketType;
use nym_sphinx::receiver::ReconstructedMessage; use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::connections::{ use nym_task::connections::{
ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane, ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane,
@@ -42,7 +41,6 @@ pub(crate) struct HandlerBuilder {
self_full_address: Recipient, self_full_address: Recipient,
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>,
} }
impl HandlerBuilder { impl HandlerBuilder {
@@ -53,7 +51,6 @@ impl HandlerBuilder {
self_full_address: &Recipient, self_full_address: &Recipient,
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>,
) -> Self { ) -> Self {
Self { Self {
msg_input, msg_input,
@@ -62,7 +59,6 @@ impl HandlerBuilder {
self_full_address: *self_full_address, self_full_address: *self_full_address,
lane_queue_lengths, lane_queue_lengths,
reply_controller_sender, reply_controller_sender,
packet_type,
} }
} }
@@ -77,7 +73,6 @@ impl HandlerBuilder {
received_response_type: Default::default(), received_response_type: Default::default(),
lane_queue_lengths: self.lane_queue_lengths.clone(), lane_queue_lengths: self.lane_queue_lengths.clone(),
reply_controller_sender: self.reply_controller_sender.clone(), reply_controller_sender: self.reply_controller_sender.clone(),
packet_type: self.packet_type,
} }
} }
} }
@@ -91,7 +86,6 @@ pub(crate) struct Handler {
received_response_type: ReceivedResponseType, received_response_type: ReceivedResponseType,
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>,
} }
impl Drop for Handler { impl Drop for Handler {
@@ -166,7 +160,7 @@ impl Handler {
}); });
// the ack control is now responsible for chunking, etc. // the ack control is now responsible for chunking, etc.
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type); let input_msg = InputMessage::new_regular(recipient, message, lane);
self.msg_input self.msg_input
.send(input_msg) .send(input_msg)
.await .await
@@ -197,8 +191,7 @@ impl Handler {
TransmissionLane::ConnectionId(id) TransmissionLane::ConnectionId(id)
}); });
let input_msg = let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane);
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
self.msg_input self.msg_input
.send(input_msg) .send(input_msg)
.await .await
@@ -225,7 +218,7 @@ impl Handler {
TransmissionLane::ConnectionId(id) TransmissionLane::ConnectionId(id)
}); });
let input_msg = InputMessage::new_reply(recipient_tag, message, lane, self.packet_type); let input_msg = InputMessage::new_reply(recipient_tag, message, lane);
self.msg_input self.msg_input
.send(input_msg) .send(input_msg)
.await .await
@@ -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
// all variable size data is always prefixed with u64 length // all variable size data is always prefixed with u64 length
@@ -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
// all variable size data is always prefixed with u64 length // all variable size data is always prefixed with u64 length
@@ -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::error::ErrorKind; use crate::error::ErrorKind;
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.23" version = "1.1.15"
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"
+36 -52
View File
@@ -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)]
@@ -94,10 +89,8 @@ impl From<Init> for OverrideConfig {
use_anonymous_replies: init_config.use_reply_surbs, use_anonymous_replies: init_config.use_reply_surbs,
fastmode: init_config.fastmode, fastmode: init_config.fastmode,
no_cover: init_config.no_cover, no_cover: init_config.no_cover,
medium_toggle: false,
nyxd_urls: init_config.nyxd_urls, nyxd_urls: init_config.nyxd_urls,
enabled_credentials_mode: init_config.enabled_credentials_mode, enabled_credentials_mode: init_config.enabled_credentials_mode,
outfox: false,
} }
} }
} }
@@ -106,19 +99,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(),
} }
} }
@@ -132,27 +121,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.
@@ -168,47 +149,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");
}
+17 -161
View File
@@ -1,25 +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::{PacketSize, PacketType};
use std::error::Error; use std::error::Error;
pub mod init; pub mod init;
@@ -72,10 +62,8 @@ pub(crate) struct OverrideConfig {
use_anonymous_replies: Option<bool>, use_anonymous_replies: Option<bool>,
fastmode: bool, fastmode: bool,
no_cover: bool, no_cover: bool,
medium_toggle: bool,
nyxd_urls: Option<Vec<url::Url>>, nyxd_urls: Option<Vec<url::Url>>,
enabled_credentials_mode: Option<bool>, enabled_credentials_mode: Option<bool>,
outfox: bool,
} }
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> { pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
@@ -92,173 +80,41 @@ 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 {
let disable_cover_traffic_with_keepalive = args.medium_toggle;
let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16);
let no_per_hop_delays = args.medium_toggle;
let packet_type = if args.outfox {
PacketType::Outfox
} else {
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(
// NOTE: This interacts with disabling cover traffic fully, so we want to this to be set before
BaseClientConfig::with_disabled_cover_traffic_with_keepalive,
disable_cover_traffic_with_keepalive,
)
.with_base(
BaseClientConfig::with_secondary_packet_size,
secondary_packet_size,
)
.with_base(BaseClientConfig::with_no_per_hop_delays, no_per_hop_delays)
// NOTE: see comment above about the order of the other disble cover traffic config
.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)]
+37 -24
View File
@@ -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.
@@ -60,18 +63,10 @@ pub(crate) struct Run {
#[clap(long, hide = true)] #[clap(long, hide = true)]
no_cover: bool, no_cover: bool,
/// Enable medium mixnet traffic, for experiments only.
/// This includes things like disabling cover traffic, no per hop delays, etc.
#[clap(long, hide = true)]
medium_toggle: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway /// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement. /// with bandwidth credential requirement.
#[clap(long, hide = true)] #[clap(long, hide = true)]
enabled_credentials_mode: Option<bool>, enabled_credentials_mode: Option<bool>,
#[clap(long, hide = true, action)]
outfox: bool,
} }
impl From<Run> for OverrideConfig { impl From<Run> for OverrideConfig {
@@ -82,10 +77,8 @@ impl From<Run> for OverrideConfig {
use_anonymous_replies: run_config.use_anonymous_replies, use_anonymous_replies: run_config.use_anonymous_replies,
fastmode: run_config.fastmode, fastmode: run_config.fastmode,
no_cover: run_config.no_cover, no_cover: run_config.no_cover,
medium_toggle: run_config.medium_toggle,
nyxd_urls: run_config.nyxd_urls, nyxd_urls: run_config.nyxd_urls,
enabled_credentials_mode: run_config.enabled_credentials_mode, enabled_credentials_mode: run_config.enabled_credentials_mode,
outfox: run_config.outfox,
} }
} }
} }
@@ -94,12 +87,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");
@@ -112,18 +106,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
} }
+94 -14
View File
@@ -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(
mut config: Config,
_args: &Upgrade,
config_version: &Version,
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)
};
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); let config_version = parse_config_version(&config);
if &config_version == package_version { if &config_version == package_version {
println!("You're using the most recent version!"); println!("You're using the most recent version!");
return; return;
} }
unimplemented_upgrade(package_version, &config_version) 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)
} }
-160
View File
@@ -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)
}
}
-20
View File
@@ -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),
}
}
}
-114
View File
@@ -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 }}'
"#;
-1
View File
@@ -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]
+1 -1
View File
@@ -1 +1 @@
18 16
+10 -21
View File
@@ -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,7 +608,7 @@ 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> {
@@ -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);
}
} }
+8 -47
View File
@@ -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,
@@ -524,7 +515,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
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);
});
-5
View File
@@ -1,5 +0,0 @@
import { Coin } from '@cosmjs/proto-signing';
export interface ISimulateClient {
simulateSend(signingAddress: string, from: string, to: string, amount: Coin[]): Promise<number>;
}
-2
View File
@@ -1,2 +0,0 @@
[build]
target = "wasm32-unknown-unknown"
+1
View File
@@ -1,5 +1,6 @@
/target /target
**/*.rs.bk **/*.rs.bk
Cargo.lock
bin/ bin/
pkg/ pkg/
wasm-pack.log wasm-pack.log
-5289
View File
File diff suppressed because it is too large Load Diff
+5 -15
View File
@@ -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,39 +17,29 @@ default = ["console_error_panic_hook"]
offline-test = [] offline-test = []
[dependencies] [dependencies]
async-trait = "0.1.68"
bs58 = "0.4.0"
futures = "0.3" futures = "0.3"
js-sys = "0.3" js-sys = "0.3"
rand = { version = "0.7.3", features = ["wasm-bindgen"] } 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"
zeroize = "1.6.0"
wasm-timer = { git = "https://github.com/mmsinclair/wasm-timer", rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"}
# internal # internal
nym-node-tester-utils = { path = "../../common/node-tester-utils" }
nym-client-core = { path = "../../common/client-core", default-features = false, features = ["wasm"] } nym-client-core = { path = "../../common/client-core", default-features = false, features = ["wasm"] }
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } 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-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 +57,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
-3
View File
@@ -3,6 +3,3 @@ clippy:
test: test:
wasm-pack test --node wasm-pack test --node
wasm-build:
wasm-pack build
@@ -1,2 +0,0 @@
node_modules
dist
-5
View File
@@ -1,5 +0,0 @@
// A dependency graph that contains any wasm must all be imported
// asynchronously. This `bootstrap.js` file does the single async import, so
// that no one else needs to worry about it again.
import('./index.js')
.catch(e => console.error('Error importing `index.js`:', e));
@@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nym WebAssembly Demo</title>
</head>
<body>
<p>
<label>Sender: </label><input disabled="true" size="85" type="text" id="sender" value="">
</p>
<p>
<label>Recipient: </label><input size="85" type="text" id="recipient" value="">
</p>
<p>
<label>Message: </label><input type="text" id="message" value="Hello mixnet!">
</p>
<p>
<button id="send-button">Send</button>
</p>
<div>
<label>Mixnode Identity: </label>
<input type="text" size = "60" id="mixnode_identity" value="...">
<button id="magic-button">✨ Magic Test Button ✨</button>
</div>
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
<p><span style='color: blue;'>Sent</span> messages show in blue, <span style='color: green;'>received</span>
messages show in green.</p>
<hr>
<p>
<span id="output"></span>
</p>
<script src="./bootstrap.js"></script>
</body>
</html>
-170
View File
@@ -1,170 +0,0 @@
// Copyright 2020-2023 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
class WebWorkerClient {
worker = null;
constructor() {
this.worker = new Worker('./worker.js');
this.worker.onmessage = (ev) => {
if (ev.data && ev.data.kind) {
switch (ev.data.kind) {
case 'Ready':
const {selfAddress} = ev.data.args;
displaySenderAddress(selfAddress);
break;
case 'ReceiveMessage':
const {message, senderTag, isTestPacket } = ev.data.args;
displayReceived(message, senderTag, isTestPacket);
break;
case 'DisableMagicTestButton':
const magicButton = document.querySelector('#magic-button');
magicButton.setAttribute('disabled', "true")
break;
case 'DisplayTesterResults':
const {score, sentPackets, receivedPackets, receivedAcks, duplicatePackets, duplicateAcks} = ev.data.args;
const resultText = `Test score: ${score}. Sent ${sentPackets} packets. Received ${receivedPackets} packets and ${receivedAcks} acks back. We also got ${duplicatePackets} duplicate packets and ${duplicateAcks} duplicate acks.`
displayReceivedRawString(resultText)
break;
}
}
};
}
sendMessage = (message, recipient) => {
if (!this.worker) {
console.error('Could not send message because worker does not exist');
return;
}
this.worker.postMessage({
kind: 'SendMessage',
args: {
message, recipient,
},
});
};
sendTestPacket = (mixnodeIdentity) => {
if (!this.worker) {
console.error('Could not send message because worker does not exist');
return;
}
this.worker.postMessage({
kind: 'TestPacket',
args: {
mixnodeIdentity,
},
});
}
}
let client = null;
async function main() {
client = new WebWorkerClient();
const sendButton = document.querySelector('#send-button');
sendButton.onclick = function () {
sendMessageTo();
};
const magicButton = document.querySelector('#magic-button');
magicButton.onclick = function () {
sendTestPacket();
}
}
/**
* Create a Sphinx packet and send it to the mixnet through the gateway node.
*
* Message and recipient are taken from the values in the user interface.
*
*/
async function sendMessageTo() {
const message = document.getElementById('message').value;
const recipient = document.getElementById('recipient').value;
await client.sendMessage(message, recipient);
displaySend(message);
}
async function sendTestPacket() {
const mixnodeIdentity = document.getElementById('mixnode_identity').value;
await client.sendTestPacket(mixnodeIdentity)
displaySend(`sending test packets to: ${mixnodeIdentity}...`);
}
/**
* Display messages that have been sent up the websocket. Colours them blue.
*
* @param {string} message
*/
function displaySend(message) {
let timestamp = new Date().toISOString().substr(11, 12);
let sendDiv = document.createElement('div');
let paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: blue');
let paragraphContent = document.createTextNode(timestamp + ' sent >>> ' + message);
paragraph.appendChild(paragraphContent);
sendDiv.appendChild(paragraph);
document.getElementById('output').appendChild(sendDiv);
}
/**
* Display received text messages in the browser. Colour them green.
*
* @param {Uint8Array} raw
*/
function displayReceived(raw, sender_tag, isTestPacket) {
let content = new TextDecoder().decode(raw);
if (sender_tag !== undefined) {
console.log("this message also contained some surbs from", sender_tag)
}
if (isTestPacket) {
const decoded = JSON.parse(content)
content = `Received packet ${decoded.msg_id} / ${decoded.total_msgs} for node ${decoded.encoded_node_identity} (test: ${decoded.test_id})`
}
displayReceivedRawString(content)
}
function displayReceivedRawString(raw) {
let timestamp = new Date().toISOString().substr(11, 12);
let receivedDiv = document.createElement('div');
let paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: green');
let paragraphContent = document.createTextNode(timestamp + ' received >>> ' + raw);
paragraph.appendChild(paragraphContent);
receivedDiv.appendChild(paragraph);
document.getElementById('output').appendChild(receivedDiv);
}
/**
* Display the nymClient's sender address in the user interface
*
* @param {String} address
*/
function displaySenderAddress(address) {
document.getElementById('sender').value = address;
}
main();
@@ -1,40 +0,0 @@
{
"name": "create-wasm-app",
"version": "0.1.0",
"description": "create an app to consume rust-generated wasm packages",
"main": "index.js",
"bin": {
"create-wasm-app": ".bin/create-wasm-app.js"
},
"scripts": {
"build": "webpack --config webpack.config.js",
"build:wasm": "cd ../ && make wasm-build",
"start": "webpack-dev-server --port 8001"
},
"repository": {
"type": "git",
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
},
"keywords": [
"webassembly",
"wasm",
"rust",
"webpack"
],
"author": "Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/nymtech/nym/issues"
},
"homepage": "https://nymtech.net/docs",
"devDependencies": {
"copy-webpack-plugin": "^10.2.4",
"hello-wasm-pack": "^0.1.0",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
},
"dependencies": {
"@nymproject/nym-client-wasm": "file:../pkg"
}
}
@@ -1,33 +0,0 @@
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
entry: {
bootstrap: './bootstrap.js',
worker: './worker.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
// mode: 'development',
mode: 'production',
plugins: [
new CopyWebpackPlugin({
patterns: [
'index.html',
{
from: 'node_modules/@nymproject/nym-client-wasm/*.(js|wasm)',
to: '[name][ext]',
},
],
}),
],
experiments: { syncWebAssembly: true },
};
-382
View File
@@ -1,382 +0,0 @@
// Copyright 2020-2023 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
importScripts("nym_client_wasm.js");
console.log("Initializing worker");
// wasm_bindgen creates a global variable (with the exports attached) that is in scope after `importScripts`
const {
NymNodeTester,
WasmGateway,
WasmMixNode,
WasmNymTopology,
default_debug,
NymClientBuilder,
NymClient,
set_panic_hook,
Config,
GatewayEndpointConfig,
ClientStorage,
current_network_topology,
make_key,
make_key2,
} = wasm_bindgen;
let client = null;
let tester = null;
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
function dummyTopology() {
const l1Mixnode = new WasmMixNode(
1,
"n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47",
"178.79.143.65",
1789,
"4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz",
"8ndjk5oZ6HxUZNScLJJ7hk39XtUqGexdKgW7hSX6kpWG",
1,
"1.10.0"
);
const l2Mixnode = new WasmMixNode(
2,
"n1z93z44vf8ssvdhujjvxcj4rd5e3lz0l60wdk70",
"109.74.197.180",
1789,
"7sVjiMrPYZrDWRujku9QLxgE8noT7NTgBAqizCsu7AoK",
"GepXwRnKZDd8x2nBWAajGGBVvF3mrpVMQBkgfrGuqRCN",
2,
"1.10.0"
);
const l3Mixnode = new WasmMixNode(
3,
"n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77",
"176.58.101.80",
1789,
"FoM5Mx9Pxk1g3zEqkS3APgtBeTtTo3M8k7Yu4bV6kK1R",
"DeYjrDC2AcQRVFshiKnbUo6bRvPyZ33QGYR2DLeFJ9qD",
3,
"1.10.0"
);
const gateway = new WasmGateway(
"n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts",
"85.159.212.96",
1789,
9000,
"336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9",
"BtYjoWihiuFihGKQypmpSspbhmWDPxzqeTVSd8ciCpWL",
"1.10.1"
);
const mixnodes = new Map();
mixnodes.set(1, [l1Mixnode]);
mixnodes.set(2, [l2Mixnode]);
mixnodes.set(3, [l3Mixnode]);
const gateways = [gateway];
return new WasmNymTopology(mixnodes, gateways);
}
function printAndDisplayTestResult(result) {
result.log_details();
self.postMessage({
kind: "DisplayTesterResults",
args: {
score: result.score(),
sentPackets: result.sent_packets,
receivedPackets: result.received_packets,
receivedAcks: result.received_acks,
duplicatePackets: result.duplicate_packets,
duplicateAcks: result.duplicate_acks,
},
});
}
async function testWithTester() {
// A) construct with hardcoded topology
const topology = dummyTopology();
const nodeTester = await new NymNodeTester(topology, preferredGateway);
// B) first get topology directly from nym-api
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// const topology = await current_network_topology(validator)
// const nodeTester = await new NymNodeTester(topology, preferredGateway);
//
// 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 nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway)
// B) first get topology directly from nym-api
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// const topology = await current_network_topology(validator)
// const nodeTester = await new NymNodeTester(topology, undefined, preferredGateway);
//
// 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 nodeTester = await NymNodeTester.new_with_api(validator, undefined, preferredGateway)
// 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) => {
if (event.data && event.data.kind) {
switch (event.data.kind) {
case "TestPacket": {
const { mixnodeIdentity } = event.data.args;
console.log("starting node test...");
let result = await nodeTester.test_node(mixnodeIdentity);
printAndDisplayTestResult(result);
}
}
}
};
}
async function testerReconnection() {
const validator = "https://qwerty-validator-api.qa.nymte.ch/api";
const nodeTester = await NymNodeTester.new_with_api(validator);
self.onmessage = async (event) => {
if (event.data && event.data.kind) {
switch (event.data.kind) {
case "TestPacket": {
const { mixnodeIdentity } = event.data.args;
console.log("starting node test...");
let result1 = await nodeTester.test_node(mixnodeIdentity);
console.log("sleeping for 5s");
await new Promise((r) => setTimeout(r, 5000));
await nodeTester.disconnect_from_gateway();
console.log("sleeping for 5s");
await new Promise((r) => setTimeout(r, 5000));
await nodeTester.reconnect_to_gateway();
let result2 = await nodeTester.test_node(mixnodeIdentity);
printAndDisplayTestResult(result1);
printAndDisplayTestResult(result2);
}
}
}
};
}
async function testWithNymClient() {
const topology = dummyTopology();
let received = 0;
const onMessageHandler = (message) => {
received += 1;
self.postMessage({
kind: "ReceiveMessage",
args: {
message,
senderTag: undefined,
isTestPacket: true,
},
});
// it's really up to the user to create proper callback here...
console.log(`received ${received} packets so far`);
};
console.log("Instantiating WASM client...");
let clientBuilder = NymClientBuilder.new_tester(
topology,
onMessageHandler,
preferredGateway
);
console.log("Web worker creating WASM client...");
let local_client = await clientBuilder.start_client();
console.log("WASM client running!");
const selfAddress = local_client.self_address();
// set the global (I guess we don't have to anymore?)
client = local_client;
console.log(`Client address is ${selfAddress}`);
self.postMessage({
kind: "Ready",
args: {
selfAddress,
},
});
// Set callback to handle messages passed to the worker.
self.onmessage = async (event) => {
console.log(event);
if (event.data && event.data.kind) {
switch (event.data.kind) {
case "SendMessage": {
const { message, recipient } = event.data.args;
let uint8Array = new TextEncoder().encode(message);
await client.send_regular_message(uint8Array, recipient);
break;
}
case "TestPacket": {
const { mixnodeIdentity } = event.data.args;
const req = await client.try_construct_test_packet_request(
mixnodeIdentity
);
await client.change_hardcoded_topology(req.injectable_topology());
await client.try_send_test_packets(req);
break;
}
}
}
};
}
async function normalNymClientUsage() {
self.postMessage({ kind: "DisableMagicTestButton" });
// only really useful if you want to adjust some settings like traffic rate
// (if not needed you can just pass a null)
const debug = default_debug();
debug.disable_main_poisson_packet_distribution = true;
debug.disable_loop_cover_traffic_stream = true;
debug.use_extended_packet_size = false;
// debug.average_packet_delay_ms = BigInt(10);
// debug.average_ack_delay_ms = BigInt(10);
// debug.ack_wait_addition_ms = BigInt(3000);
// debug.ack_wait_multiplier = 10;
debug.topology_refresh_rate_ms = BigInt(60000);
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const validator = "https://qwerty-validator-api.qa.nymte.ch/api";
const config = new Config("my-awesome-wasm-client", validator, debug);
const onMessageHandler = (message) => {
console.log(message);
self.postMessage({
kind: "ReceiveMessage",
args: {
message,
},
});
};
console.log("Instantiating WASM client...");
let localClient = await new NymClient(config, onMessageHandler);
console.log("WASM client running!");
const selfAddress = localClient.self_address();
// set the global (I guess we don't have to anymore?)
client = localClient;
console.log(`Client address is ${selfAddress}`);
self.postMessage({
kind: "Ready",
args: {
selfAddress,
},
});
// Set callback to handle messages passed to the worker.
self.onmessage = async (event) => {
console.log(event);
if (event.data && event.data.kind) {
switch (event.data.kind) {
case "SendMessage": {
const { message, recipient } = event.data.args;
let uint8Array = new TextEncoder().encode(message);
await client.send_regular_message(uint8Array, recipient);
break;
}
}
}
};
}
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() {
// load WASM package
await wasm_bindgen("nym_client_wasm_bg.wasm");
console.log("Loaded WASM");
// sets up better stack traces in case of in-rust panics
set_panic_hook();
// show reconnection capabilities
// await testerReconnection()
// run test on simplified and dedicated tester:
await testWithTester();
// 'Normal' client setup (to send 'normal' messages)
// await normalNymClientUsage()
}
// Let's get started!
main();
File diff suppressed because it is too large Load Diff
+72 -81
View File
@@ -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,58 @@
#![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: 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: 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 +77,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 +94,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 +127,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 +139,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 +153,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 +169,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 +180,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 +198,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 +208,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 +220,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,
@@ -231,38 +229,31 @@ pub struct TopologyWasm {
/// path. This timeout determines waiting period until it is decided that the packet /// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination. /// did not reach its destination.
pub topology_resolution_timeout_ms: u64, pub topology_resolution_timeout_ms: u64,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
} }
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(
topology.topology_resolution_timeout_ms, topology.topology_resolution_timeout_ms,
), ),
disable_refreshing: topology.disable_refreshing,
} }
} }
} }
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,
} }
} }
} }
#[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 +287,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 +308,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 +332,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 +365,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 +379,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()
} }
+6 -134
View File
@@ -1,42 +1,16 @@
// 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::error::WasmClientError;
use crate::tester::helpers::WasmTestMessageExt;
use crate::tester::{NodeTestMessage, DEFAULT_TEST_PACKETS};
use crate::topology::WasmNymTopology;
use js_sys::Promise; use js_sys::Promise;
use nym_client_core::client::base_client::{ClientInput, ClientState}; use nym_client_core::client::base_client::ClientInput;
use nym_client_core::client::inbound_messages::InputMessage; use nym_client_core::client::inbound_messages::InputMessage;
use nym_topology::{MixLayer, NymTopology};
use std::sync::Arc; use std::sync::Arc;
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};
#[wasm_bindgen]
pub struct NymClientTestRequest {
// serialized NodeTestMessage
pub(crate) test_msgs: Vec<Vec<u8>>,
// specially constructed network topology that only contains the target
// node on the tested layer
pub(crate) testable_topology: NymTopology,
}
#[wasm_bindgen]
impl NymClientTestRequest {
pub fn injectable_topology(&self) -> WasmNymTopology {
self.testable_topology.clone().into()
}
}
// defining helper trait as we could directly call the method on the wrapper // defining helper trait as we could directly call the method on the wrapper
pub(crate) trait InputSender { pub(crate) trait InputSender {
fn send_message(&self, message: InputMessage) -> Promise; fn send_message(&self, message: InputMessage) -> Promise;
fn send_messages(&self, messages: Vec<InputMessage>) -> Promise;
} }
impl InputSender for Arc<ClientInput> { impl InputSender for Arc<ClientInput> {
@@ -45,114 +19,12 @@ impl InputSender for Arc<ClientInput> {
future_to_promise(async move { future_to_promise(async move {
match this.input_sender.send(message).await { match this.input_sender.send(message).await {
Ok(_) => Ok(JsValue::null()), Ok(_) => Ok(JsValue::null()),
Err(_) => Err(simple_js_error( Err(_) => {
"InputMessageReceiver has stopped receiving!", let js_error =
)), js_sys::Error::new("InputMessageReceiver has stopped receiving!");
} Err(JsValue::from(js_error))
})
}
fn send_messages(&self, messages: Vec<InputMessage>) -> Promise {
let this = Arc::clone(self);
future_to_promise(async move {
for message in messages {
if this.input_sender.send(message).await.is_err() {
return Err(simple_js_error(
"InputMessageReceiver has stopped receiving!",
));
} }
} }
Ok(JsValue::null())
})
}
}
pub(crate) trait WasmTopologyExt {
/// Changes the current network topology to the provided value.
fn change_hardcoded_topology(&self, topology: WasmNymTopology) -> Promise;
/// Returns the current network topology.
fn current_topology(&self) -> Promise;
/// Checks whether the provided node exists in the known network topology and if so, returns its layer.
fn check_for_mixnode_existence(&self, mixnode_identity: String) -> Promise;
/// Creates a `NymClientTestRequest` with a variant of `this` topology where the target node is the only one on its layer.
fn mix_test_request(
&self,
test_id: u32,
mixnode_identity: String,
num_test_packets: Option<u32>,
) -> Promise;
}
impl WasmTopologyExt for Arc<ClientState> {
fn change_hardcoded_topology(&self, topology: WasmNymTopology) -> Promise {
let this = Arc::clone(self);
future_to_promise(async move {
let nym_topology: NymTopology = topology.into();
console_log!("changing topology to {nym_topology:?}");
this.topology_accessor
.manually_change_topology(nym_topology)
.await;
Ok(JsValue::null())
})
}
fn current_topology(&self) -> Promise {
let this = Arc::clone(self);
future_to_promise(async move {
match this.topology_accessor.current_topology().await {
Some(topology) => Ok(JsValue::from(WasmNymTopology::from(topology))),
None => Err(WasmClientError::UnavailableNetworkTopology.into()),
}
})
}
/// Checks whether the target mixnode exists in the known network topology and returns its layer.
fn check_for_mixnode_existence(&self, mixnode_identity: String) -> Promise {
let this = Arc::clone(self);
future_to_promise(async move {
let Some(current_topology) = this.topology_accessor.current_topology().await else {
return Err(WasmClientError::UnavailableNetworkTopology.into())
};
match current_topology.find_mix_by_identity(&mixnode_identity) {
None => Err(WasmClientError::NonExistentMixnode { mixnode_identity }.into()),
Some(node) => Ok(JsValue::from(MixLayer::from(node.layer))),
}
})
}
fn mix_test_request(
&self,
test_id: u32,
mixnode_identity: String,
num_test_packets: Option<u32>,
) -> Promise {
let num_test_packets = num_test_packets.unwrap_or(DEFAULT_TEST_PACKETS);
let this = Arc::clone(self);
future_to_promise(async move {
let Some(current_topology) = this.topology_accessor.current_topology().await else {
return Err(WasmClientError::UnavailableNetworkTopology.into())
};
let Some(mix) = current_topology.find_mix_by_identity(&mixnode_identity) else {
return Err(WasmClientError::NonExistentMixnode { mixnode_identity }.into());
};
let ext = WasmTestMessageExt::new(test_id);
let test_msgs = NodeTestMessage::mix_plaintexts(mix, num_test_packets, ext)
.map_err(WasmClientError::from)?;
let mut updated = current_topology.clone();
updated.set_mixes_in_layer(mix.layer.into(), vec![mix.to_owned()]);
Ok(JsValue::from(NymClientTestRequest {
test_msgs,
testable_topology: updated,
}))
}) })
} }
} }
+106 -191
View File
@@ -1,37 +1,27 @@
// 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 self::config::Config; use self::config::Config;
use crate::client::helpers::{InputSender, NymClientTestRequest, WasmTopologyExt}; use crate::client::helpers::InputSender;
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::helpers::{
parse_recipient, parse_sender_tag, setup_from_topology, setup_gateway_from_api,
setup_reply_surb_storage_backend,
};
use crate::storage::traits::FullWasmClientStorage;
use crate::storage::ClientStorage;
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, 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_sphinx::params::PacketType; use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
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::NymTopology;
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::RngCore;
use std::sync::Arc; use std::sync::Arc;
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::{console_error, console_log};
pub mod config; pub mod config;
mod helpers; mod helpers;
@@ -41,222 +31,147 @@ mod response_pusher;
pub struct NymClient { pub struct NymClient {
self_address: String, self_address: String,
client_input: Arc<ClientInput>, client_input: Arc<ClientInput>,
client_state: Arc<ClientState>,
// keep track of the "old" topology for the purposes of node tester
// so that it could be restored after the check is done
_full_topology: Option<NymTopology>,
// 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: PacketType,
} }
#[wasm_bindgen] #[wasm_bindgen]
pub struct NymClientBuilder { pub struct NymClientBuilder {
config: Config, config: Config,
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,
} }
#[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: Self::setup_reply_surb_storage_backend(&config),
config, config,
custom_topology: None, key_manager: Self::setup_key_manager(),
storage_passphrase,
on_message, on_message,
preferred_gateway, bandwidth_controller: None,
disabled_credentials: true,
} }
} }
// no cover traffic // TODO: once we make keys persistent, we'll require some kind of `init` method to generate
// no poisson delay // a prior shared keypair between the client and the gateway
// hardcoded topology
// NOTE: you most likely want to use `[NymNodeTester]` instead. // perhaps this should be public?
pub fn new_tester( fn setup_key_manager() -> KeyManager {
topology: WasmNymTopology, let mut rng = OsRng;
on_message: js_sys::Function, // for time being generate new keys each time...
gateway: Option<IdentityKey>, console_log!("generated new set of keys");
) -> Self { KeyManager::new(&mut rng)
if let Some(gateway_id) = &gateway {
if !topology.ensure_contains_gateway_id(gateway_id) {
panic!("the specified topology does not contain the gateway used by the client")
}
} }
let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID); // don't get too excited about the name, under the hood it's just a big fat placeholder
// with no persistence
NymClientBuilder { fn setup_reply_surb_storage_backend(config: &Config) -> browser_backend::Backend {
config: full_config, browser_backend::Backend::new(
custom_topology: Some(topology.into()), config
on_message, .debug
storage_passphrase: None, .reply_surbs
preferred_gateway: gateway, .minimum_reply_surb_storage_threshold,
} config
.debug
.reply_surbs
.maximum_reply_surb_storage_threshold,
)
} }
fn start_reconstructed_pusher(client_output: ClientOutput, on_message: js_sys::Function) { fn start_reconstructed_pusher(client_output: ClientOutput, on_message: js_sys::Function) {
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>> { pub async fn start_client(self) -> Promise {
if let Some(hardcoded_topology) = self.custom_topology.take() { future_to_promise(async move {
Some(Box::new(HardcodedTopologyProvider::new(hardcoded_topology)))
} else {
None
}
}
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> {
console_log!("Starting the wasm client"); console_log!("Starting the wasm client");
let nym_api_endpoints = self.config.base.client.nym_api_urls.clone(); let disabled_credentials = if self.disabled_credentials {
CredentialsToggle::Disabled
// 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 { } else {
setup_gateway_from_api(&client_store, user_chosen, &nym_api_endpoints).await? CredentialsToggle::Enabled
}; };
let packet_type = self.config.base.debug.traffic.packet_type; let base_builder = BaseClientBuilder::new(
let storage = Self::initialise_storage(&self.config, client_store); &self.config.gateway_endpoint,
let maybe_topology_provider = self.topology_provider(); &self.config.debug,
self.key_manager,
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = self.bandwidth_controller,
BaseClientBuilder::<FakeClient<DirectSigningNyxdClient>, _>::new( self.reply_surb_storage_backend,
&self.config.base, disabled_credentials,
storage, vec![self.config.nym_api_url.clone()],
None,
); );
if let Some(topology_provider) = maybe_topology_provider {
base_builder = base_builder.with_topology_provider(topology_provider);
}
let mut started_client = base_builder.start_base().await?; let self_address = base_builder.as_mix_recipient().to_string();
let self_address = started_client.address.to_string(); let mut started_client = match base_builder.start_base().await {
Ok(base_client) => base_client,
Err(err) => {
let error_msg = format!("failed to start the base client components - {err}");
console_error!("{}", error_msg);
let js_error = js_sys::Error::new(&error_msg);
return Err(JsValue::from(js_error));
}
};
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();
Self::start_reconstructed_pusher(client_output, self.on_message); Self::start_reconstructed_pusher(client_output, self.on_message);
Ok(NymClient { Ok(JsValue::from(NymClient {
self_address, self_address,
client_input: Arc::new(client_input), client_input: Arc::new(client_input),
client_state: Arc::new(started_client.client_state),
_full_topology: None,
_task_manager: started_client.task_manager, _task_manager: started_client.task_manager,
packet_type, }))
}) })
} }
pub fn start_client(self) -> Promise {
future_to_promise(async move { self.start_client_async().await.into_promise_result() })
}
} }
#[wasm_bindgen] #[wasm_bindgen]
impl NymClient { impl NymClient {
async fn _new(
config: Config,
on_message: js_sys::Function,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>,
) -> Result<NymClient, WasmClientError> {
NymClientBuilder::new(config, on_message, preferred_gateway, storage_passphrase)
.start_client_async()
.await
}
#[wasm_bindgen(constructor)]
#[allow(clippy::new_ret_no_self)]
pub fn new(
config: Config,
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 {
self.self_address.clone() self.self_address.clone()
} }
pub fn try_construct_test_packet_request( fn parse_recipient(recipient: &str) -> Result<Recipient, JsValue> {
&self, match Recipient::try_from_base58_string(recipient) {
mixnode_identity: String, Ok(recipient) => Ok(recipient),
num_test_packets: Option<u32>, Err(err) => {
) -> Promise { let error_msg = format!("{recipient} is not a valid Nym network recipient - {err}");
// TODO: improve the source of rng (i.e. don't make it ephemeral...) console_error!("{}", error_msg);
let mut ephemeral_rng = OsRng; let js_error = js_sys::Error::new(&error_msg);
let test_id = ephemeral_rng.next_u32(); Err(JsValue::from(js_error))
self.client_state }
.mix_test_request(test_id, mixnode_identity, num_test_packets) }
} }
pub fn change_hardcoded_topology(&self, topology: WasmNymTopology) -> Promise { fn parse_sender_tag(tag: &str) -> Result<AnonymousSenderTag, JsValue> {
self.client_state.change_hardcoded_topology(topology) match AnonymousSenderTag::try_from_base58_string(tag) {
Ok(tag) => Ok(tag),
Err(err) => {
let error_msg = format!("{tag} is not a valid Nym AnonymousSenderTag - {err}");
console_error!("{}", error_msg);
let js_error = js_sys::Error::new(&error_msg);
Err(JsValue::from(js_error))
} }
pub fn current_network_topology(&self) -> Promise {
self.client_state.current_topology()
} }
/// Sends a test packet through the current network topology.
/// It's the responsibility of the caller to ensure the correct topology has been injected and
/// correct onmessage handlers have been setup.
pub fn try_send_test_packets(&mut self, request: NymClientTestRequest) -> Promise {
// TOOD: use the premade packets instead
console_log!(
"Attempting to send {} test packets",
request.test_msgs.len()
);
// our address MUST BE valid
let recipient = parse_recipient(&self.self_address()).unwrap();
let lane = TransmissionLane::General;
let input_msgs = request
.test_msgs
.into_iter()
.map(|p| InputMessage::new_regular(recipient, p, lane, None))
.collect();
self.client_input.send_messages(input_msgs)
} }
/// The simplest message variant where no additional information is attached. /// The simplest message variant where no additional information is attached.
@@ -269,11 +184,13 @@ impl NymClient {
message.len() as f64 / 1024.0 message.len() as f64 / 1024.0
); );
let recipient = check_promise_result!(parse_recipient(&recipient)); let recipient = match Self::parse_recipient(&recipient) {
Ok(recipient) => recipient,
Err(err) => return Promise::reject(&err),
};
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.client_input.send_message(input_msg) self.client_input.send_message(input_msg)
} }
@@ -296,17 +213,13 @@ impl NymClient {
message.len() as f64 / 1024.0 message.len() as f64 / 1024.0
); );
let recipient = check_promise_result!(parse_recipient(&recipient)); let recipient = match Self::parse_recipient(&recipient) {
Ok(recipient) => recipient,
Err(err) => return Promise::reject(&err),
};
let lane = TransmissionLane::General; let lane = TransmissionLane::General;
let input_msg = InputMessage::new_anonymous( let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane);
recipient,
message,
reply_surbs,
lane,
Some(self.packet_type),
);
self.client_input.send_message(input_msg) self.client_input.send_message(input_msg)
} }
@@ -320,11 +233,13 @@ impl NymClient {
message.len() as f64 / 1024.0 message.len() as f64 / 1024.0
); );
let sender_tag = check_promise_result!(parse_sender_tag(&recipient_tag)); let sender_tag = match Self::parse_sender_tag(&recipient_tag) {
Ok(recipient) => recipient,
Err(err) => return Promise::reject(&err),
};
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.client_input.send_message(input_msg) self.client_input.send_message(input_msg)
} }
} }
-5
View File
@@ -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";
-122
View File
@@ -1,122 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::storage::errors::ClientStorageError;
use crate::topology::WasmTopologyError;
use js_sys::Promise;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError;
use nym_node_tester_utils::error::NetworkTestingError;
use nym_sphinx::addressing::clients::RecipientFormattingError;
use nym_sphinx::anonymous_replies::requests::InvalidAnonymousSenderTagRepresentation;
use nym_topology::NymTopologyError;
use nym_validator_client::ValidatorClientError;
use thiserror::Error;
use wasm_bindgen::JsValue;
use wasm_utils::simple_js_error;
// might as well start using well-defined error enum...
#[derive(Debug, Error)]
pub enum WasmClientError {
#[error(
"A node test is already in progress. Wait for it to finish before starting another one."
)]
TestInProgress,
#[error("experienced an issue with internal client components: {source}")]
BaseClientError {
#[from]
source: ClientCoreError,
},
#[error("The provided gateway identity is invalid: {source}")]
InvalidGatewayIdentity { source: Ed25519RecoveryError },
#[error("Gateway communication failure: {source}")]
GatewayClientError {
#[from]
source: GatewayClientError,
},
#[error("failed to query nym api: {source}")]
NymApiError {
#[from]
source: ValidatorClientError,
},
#[error("The provided wasm topology was invalid: {source}")]
WasmTopologyError {
#[from]
source: WasmTopologyError,
},
#[error("The provided nym topology was invalid: {source}")]
TopologyError {
#[from]
source: NymTopologyError,
},
#[error("failed to test the node: {source}")]
NodeTestingFailure {
#[from]
source: NetworkTestingError,
},
#[error("{raw} is not a valid url: {source}")]
MalformedUrl {
raw: String,
source: url::ParseError,
},
#[error("Network topology is currently unavailable")]
UnavailableNetworkTopology,
#[error("Mixnode {mixnode_identity} is not present in the current network topology")]
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}")]
MalformedRecipient {
raw: String,
source: RecipientFormattingError,
},
#[error("{raw} is not a valid Nym AnonymousSenderTag: {source}")]
MalformedSenderTag {
raw: String,
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 {
pub fn into_rejected_promise(self) -> Promise {
self.into()
}
}
impl From<WasmClientError> for JsValue {
fn from(value: WasmClientError) -> Self {
simple_js_error(value.to_string())
}
}
impl From<WasmClientError> for Promise {
fn from(value: WasmClientError) -> Self {
Promise::reject(&value.into())
}
}
-114
View File
@@ -1,114 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::WasmClientError;
use crate::storage::ClientStorage;
use crate::topology::WasmNymTopology;
use js_sys::Promise;
use nym_client_core::client::replies::reply_storage::browser_backend;
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::anonymous_replies::requests::AnonymousSenderTag;
use nym_topology::{gateway, NymTopology};
use nym_validator_client::client::IdentityKey;
use nym_validator_client::NymApiClient;
use rand::thread_rng;
use url::Url;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::PromisableResult;
// don't get too excited about the name, under the hood it's just a big fat placeholder
// with no disk_persistence
pub(crate) fn setup_reply_surb_storage_backend(
config: config::ReplySurbs,
) -> browser_backend::Backend {
browser_backend::Backend::new(
config.minimum_reply_surb_storage_threshold,
config.maximum_reply_surb_storage_threshold,
)
}
pub(crate) fn parse_recipient(recipient: &str) -> Result<Recipient, WasmClientError> {
Recipient::try_from_base58_string(recipient).map_err(|source| {
WasmClientError::MalformedRecipient {
raw: recipient.to_string(),
source,
}
})
}
pub(crate) fn parse_sender_tag(tag: &str) -> Result<AnonymousSenderTag, WasmClientError> {
AnonymousSenderTag::try_from_base58_string(tag).map_err(|source| {
WasmClientError::MalformedSenderTag {
raw: tag.to_string(),
source,
}
})
}
pub(crate) async fn current_network_topology_async(
nym_api_url: String,
) -> Result<WasmNymTopology, WasmClientError> {
let url: Url = match nym_api_url.parse() {
Ok(url) => url,
Err(source) => {
return Err(WasmClientError::MalformedUrl {
raw: nym_api_url,
source,
})
}
};
let api_client = NymApiClient::new(url);
let mixnodes = api_client.get_cached_active_mixnodes().await?;
let gateways = api_client.get_cached_gateways().await?;
Ok(NymTopology::from_detailed(mixnodes, gateways).into())
}
#[wasm_bindgen]
pub fn current_network_topology(nym_api_url: String) -> Promise {
future_to_promise(async move {
current_network_topology_async(nym_api_url)
.await
.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 -14
View File
@@ -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::*;
@@ -8,23 +8,10 @@ mod client;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub mod encoded_payload_helper; pub mod encoded_payload_helper;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub mod error;
#[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;
#[cfg(target_arch = "wasm32")]
pub mod topology;
#[cfg(target_arch = "wasm32")]
pub mod validation; pub mod validation;
#[cfg(target_arch = "wasm32")]
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
-28
View File
@@ -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())
}
}
-294
View File
@@ -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)
}
}
-101
View File
@@ -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,112 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::tester::helpers::{NodeTestResult, WasmTestMessageExt};
use futures::StreamExt;
use nym_node_tester_utils::processor::Received;
use nym_node_tester_utils::receiver::ReceivedReceiver;
use nym_sphinx::chunking::fragment::FragmentIdentifier;
use std::collections::HashSet;
use std::time::Duration;
use tokio::sync::MutexGuard as AsyncMutexGuard;
use wasm_utils::{console_error, console_log, console_warn};
pub(crate) struct EphemeralTestReceiver<'a> {
sent_packets: u32,
expected_acks: HashSet<FragmentIdentifier>,
received_valid_messages: HashSet<u32>,
received_valid_acks: HashSet<FragmentIdentifier>,
duplicate_packets: u32,
duplicate_acks: u32,
timeout_duration: Duration,
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver<WasmTestMessageExt>>,
}
impl<'a> EphemeralTestReceiver<'a> {
pub(crate) fn finish(self) -> NodeTestResult {
NodeTestResult {
sent_packets: self.sent_packets,
received_packets: self.received_valid_messages.len() as u32,
received_acks: self.received_valid_acks.len() as u32,
duplicate_packets: self.duplicate_packets,
duplicate_acks: self.duplicate_acks,
}
}
pub(crate) fn new(
sent_packets: u32,
expected_acks: HashSet<FragmentIdentifier>,
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver<WasmTestMessageExt>>,
timeout: Duration,
) -> Self {
EphemeralTestReceiver {
sent_packets,
expected_acks,
received_valid_messages: Default::default(),
received_valid_acks: Default::default(),
duplicate_packets: 0,
duplicate_acks: 0,
timeout_duration: timeout,
receiver_permit,
}
}
fn on_next_received_packet(&mut self, packet: Option<Received<WasmTestMessageExt>>) -> bool {
let Some(received_packet) = packet else {
// can't do anything more...
console_error!("packet receiver has stopped processing results!");
return true
};
match received_packet {
Received::Message(msg) => {
if !self.received_valid_messages.insert(msg.msg_id) {
self.duplicate_packets += 1;
}
}
Received::Ack(frag_id) => {
if self.expected_acks.contains(&frag_id) {
if !self.received_valid_acks.insert(frag_id) {
self.duplicate_acks += 1
}
} else {
console_warn!("received an ack that was not part of the test! (id: {frag_id})")
}
}
}
if self.received_all() {
console_log!("already received all the packets! finishing the test...");
true
} else {
false
}
}
fn received_all(&self) -> bool {
self.received_valid_acks.len() == self.received_valid_messages.len()
&& self.received_valid_acks.len() == self.sent_packets as usize
}
pub(crate) async fn perform_test(mut self) -> NodeTestResult {
let mut timeout_fut = wasm_timer::Delay::new(self.timeout_duration);
loop {
tokio::select! {
_ = &mut timeout_fut => {
console_warn!("reached test timeout before receiving all packets.");
break
}
received_packet = self.receiver_permit.next() => {
let is_done = self.on_next_received_packet(received_packet);
if is_done {
break
}
}
}
}
self.finish()
}
}
-147
View File
@@ -1,147 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
#![allow(clippy::drop_non_drop)]
use crate::error::WasmClientError;
use crate::tester::LockedGatewayClient;
use js_sys::Promise;
use nym_node_tester_utils::processor::Received;
use nym_node_tester_utils::receiver::ReceivedReceiver;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_log, console_warn};
#[derive(Clone)]
pub(super) struct ReceivedReceiverWrapper(Arc<AsyncMutex<ReceivedReceiver<WasmTestMessageExt>>>);
impl ReceivedReceiverWrapper {
pub(super) fn new(inner: ReceivedReceiver<WasmTestMessageExt>) -> Self {
ReceivedReceiverWrapper(Arc::new(AsyncMutex::new(inner)))
}
pub(super) async fn clear_received_channel(&self) {
let mut lost_msgs = 0;
let mut lost_acks = 0;
let mut permit = self.0.lock().await;
while let Ok(Some(received)) = permit.try_next() {
match received {
Received::Message(_) => lost_msgs += 1,
Received::Ack(_) => lost_acks += 1,
}
}
if lost_msgs > 0 || lost_acks > 0 {
console_warn!("while preparing for the test run, we cleared {lost_msgs} messages and {lost_acks} acks that were received in the meantime.")
}
}
pub(super) async fn lock(&self) -> AsyncMutexGuard<'_, ReceivedReceiver<WasmTestMessageExt>> {
self.0.lock().await
}
}
#[derive(Serialize, Deserialize, Copy, Clone)]
pub struct WasmTestMessageExt {
pub test_id: u32,
}
impl WasmTestMessageExt {
pub fn new(test_id: u32) -> Self {
WasmTestMessageExt { test_id }
}
}
// TODO: maybe put it in the tester utils
#[wasm_bindgen]
pub struct NodeTestResult {
pub sent_packets: u32,
pub received_packets: u32,
pub received_acks: u32,
pub duplicate_packets: u32,
pub duplicate_acks: u32,
}
impl Display for NodeTestResult {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Test results: ")?;
writeln!(f, "Total score: {:.2}%", self.score())?;
writeln!(f, "Sent packets: {}", self.sent_packets)?;
writeln!(f, "Received (valid) packets: {}", self.received_packets)?;
writeln!(f, "Received (valid) acks: {}", self.received_acks)?;
writeln!(f, "Received duplicate packets: {}", self.duplicate_packets)?;
write!(f, "Received duplicate acks: {}", self.duplicate_acks)
}
}
#[wasm_bindgen]
impl NodeTestResult {
pub fn log_details(&self) {
console_log!("{}", self)
}
pub fn score(&self) -> f32 {
let expected = self.sent_packets * 2;
let actual = (self.received_packets + self.received_acks)
.saturating_sub(self.duplicate_packets + self.duplicate_acks);
actual as f32 / expected as f32 * 100.
}
}
pub(crate) struct TestMarker {
value: Arc<AtomicBool>,
}
impl TestMarker {
pub fn new(value: Arc<AtomicBool>) -> Self {
Self { value }
}
}
impl Drop for TestMarker {
// make sure to clear the test flag when the marker is dropped
fn drop(&mut self) {
self.value.store(false, Ordering::SeqCst)
}
}
pub(crate) trait GatewayReconnection {
fn disconnect_from_gateway(&self) -> Promise;
fn reconnect_to_gateway(&self) -> Promise;
}
impl GatewayReconnection for LockedGatewayClient {
fn disconnect_from_gateway(&self) -> Promise {
let this = self.clone();
future_to_promise(async move {
let mut guard = this.lock().await;
guard
.disconnect()
.await
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
Ok(JsValue::undefined())
})
}
fn reconnect_to_gateway(&self) -> Promise {
let this = self.clone();
future_to_promise(async move {
let mut guard = this.lock().await;
guard
.try_reconnect()
.await
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
Ok(JsValue::undefined())
})
}
}
-352
View File
@@ -1,352 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::constants::NODE_TESTER_ID;
use crate::error::WasmClientError;
use crate::helpers::{current_network_topology_async, setup_from_topology};
use crate::storage::ClientStorage;
use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
use crate::tester::helpers::{
GatewayReconnection, NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
};
use crate::topology::WasmNymTopology;
use futures::channel::mpsc;
use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::key_manager::ManagedKeys;
use nym_client_core::init::InitialisationDetails;
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_gateway_client::GatewayClient;
use nym_node_tester_utils::receiver::SimpleMessageReceiver;
use nym_node_tester_utils::{NodeTester, TestMessage};
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_sphinx::params::PacketSize;
use nym_sphinx::preparer::PreparedFragment;
use nym_task::TaskManager;
use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng;
use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex as SyncMutex};
use std::time::Duration;
use tokio::sync::Mutex as AsyncMutex;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{check_promise_result, console_log, PromisableResult};
mod ephemeral_receiver;
pub(crate) mod helpers;
pub type NodeTestMessage = TestMessage<WasmTestMessageExt>;
type LockedGatewayClient =
Arc<AsyncMutex<GatewayClient<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>>;
pub(crate) const DEFAULT_TEST_TIMEOUT: Duration = Duration::from_secs(10);
pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20;
#[wasm_bindgen]
pub struct NymNodeTester {
test_in_progress: Arc<AtomicBool>,
// we need to increment the nonce between tests to distinguish the packets
// but we can't make the tester mutable because of wasm...
// so we're using the atomics
current_test_nonce: AtomicU32,
// blame all those mutexes on being unable to have an async method with internal mutability...
tester: Arc<SyncMutex<NodeTester<OsRng>>>,
gateway_client: LockedGatewayClient,
// we have to put it behind the lock due to wasm limitations and borrowing...
// the mutex acquisition should be instant as there aren't going to be any threads attempting
// to get simultaneous access
processed_receiver: ReceivedReceiverWrapper,
// 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
_task_manager: TaskManager,
}
#[wasm_bindgen]
pub struct NymNodeTesterBuilder {
gateway: Option<IdentityKey>,
id: Option<String>,
base_topology: NymTopology,
// unimplemented
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
}
fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
Recipient::new(
*keys.identity_public_key(),
*keys.encryption_public_key(),
gateway_identity,
)
}
#[wasm_bindgen]
impl NymNodeTesterBuilder {
#[wasm_bindgen(constructor)]
pub fn new(
base_topology: WasmNymTopology,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> NymNodeTesterBuilder {
NymNodeTesterBuilder {
id,
gateway,
base_topology: base_topology.into(),
bandwidth_controller: None,
}
}
async fn _new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> {
let topology = current_network_topology_async(api_url).await?;
Ok(NymNodeTesterBuilder::new(topology, id, gateway))
}
pub fn new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
future_to_promise(async move {
Self::_new_with_api(api_url, id, gateway)
.await
.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> {
let task_manager = TaskManager::default();
let storage_id = if let Some(client_id) = &self.id {
format!("{NODE_TESTER_ID}-{client_id}")
} else {
NODE_TESTER_ID.to_owned()
};
let client_store = ClientStorage::new_async(&storage_id, None).await?;
let init_details = self.gateway_info(&client_store).await?;
let gateway_endpoint = init_details.gateway_details;
let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?;
let managed_keys = init_details.managed_keys;
let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded();
let (ack_sender, ack_receiver) = mpsc::unbounded();
let mut gateway_client = GatewayClient::new(
gateway_endpoint.gateway_listener,
managed_keys.identity_keypair(),
gateway_identity,
Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender,
ack_sender,
Duration::from_secs(10),
self.bandwidth_controller.take(),
task_manager.subscribe(),
);
gateway_client.set_disabled_credentials_mode(true);
gateway_client.authenticate_and_start().await?;
// TODO: make those values configurable later
let tester = NodeTester::new(
OsRng,
self.base_topology,
Some(address(&managed_keys, gateway_identity)),
PacketSize::default(),
Duration::from_millis(5),
Duration::from_millis(5),
managed_keys.ack_key(),
);
let (processed_sender, processed_receiver) = mpsc::unbounded();
let mut receiver = SimpleMessageReceiver::new_sphinx_receiver(
managed_keys.encryption_keypair(),
managed_keys.ack_key(),
mixnet_message_receiver,
ack_receiver,
processed_sender,
task_manager.subscribe(),
);
nym_task::spawn(async move { receiver.run().await });
Ok(NymNodeTester {
test_in_progress: Arc::new(AtomicBool::new(false)),
current_test_nonce: Default::default(),
tester: Arc::new(SyncMutex::new(tester)),
gateway_client: Arc::new(AsyncMutex::new(gateway_client)),
processed_receiver: ReceivedReceiverWrapper::new(processed_receiver),
_task_manager: task_manager,
})
}
pub fn setup_client(self) -> Promise {
future_to_promise(async move { self._setup_client().await.into_promise_result() })
}
}
async fn test_mixnode(
test_packets: Vec<PreparedFragment>,
gateway_client: LockedGatewayClient,
processed_receiver: ReceivedReceiverWrapper,
_test_marker: TestMarker,
timeout: Duration,
) -> Result<NodeTestResult, WasmClientError> {
let num_test_packets = test_packets.len() as u32;
let expected_ack_ids = test_packets
.iter()
.map(|p| p.fragment_identifier)
.collect::<HashSet<_>>();
let mix_packets = test_packets.into_iter().map(|p| p.mix_packet).collect();
// start by clearing any messages that might have been received between tests
processed_receiver.clear_received_channel().await;
// locking the gateway client so that we could get mutable access to data without having to declare
// self mutable
let mut gateway_permit = gateway_client.lock().await;
gateway_permit.batch_send_mix_packets(mix_packets).await?;
let receiver_permit = processed_receiver.lock().await;
let result =
EphemeralTestReceiver::new(num_test_packets, expected_ack_ids, receiver_permit, timeout)
.perform_test()
.await;
Ok(result)
}
#[wasm_bindgen]
impl NymNodeTester {
#[wasm_bindgen(constructor)]
#[allow(clippy::new_ret_no_self)]
pub fn new(
topology: WasmNymTopology,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
console_log!("constructing node tester!");
NymNodeTesterBuilder::new(topology, id, gateway).setup_client()
}
async fn _new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> {
NymNodeTesterBuilder::_new_with_api(api_url, id, gateway)
.await?
._setup_client()
.await
}
pub fn new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
future_to_promise(async move {
Self::_new_with_api(api_url, id, gateway)
.await
.into_promise_result()
})
}
pub fn disconnect_from_gateway(&self) -> Promise {
self.gateway_client.disconnect_from_gateway()
}
pub fn reconnect_to_gateway(&self) -> Promise {
self.gateway_client.reconnect_to_gateway()
}
fn prepare_test_packets(
&self,
mixnode_identity: String,
test_nonce: u32,
num_test_packets: u32,
) -> Result<Vec<PreparedFragment>, WasmClientError> {
let test_ext = WasmTestMessageExt::new(test_nonce);
let mut tester_permit = self.tester.lock().expect("mutex got poisoned");
tester_permit
.existing_identity_mixnode_test_packets(
mixnode_identity,
test_ext,
num_test_packets,
None,
)
.map_err(Into::into)
}
pub fn test_node(
&self,
mixnode_identity: String,
timeout_millis: Option<u64>,
num_test_packets: Option<u32>,
) -> Promise {
// establish test parameters
let timeout = timeout_millis
.map(Duration::from_millis)
.unwrap_or(DEFAULT_TEST_TIMEOUT);
let num_test_packets = num_test_packets.unwrap_or(DEFAULT_TEST_PACKETS);
// mark start of the test
if self.test_in_progress.swap(true, Ordering::SeqCst) {
return WasmClientError::TestInProgress.into_rejected_promise();
}
// prepare test packets
// (I simultaneously feel both disgusted and amazed by this workaround)
let test_nonce = self.current_test_nonce.fetch_add(1, Ordering::Relaxed);
let test_packets = check_promise_result!(self.prepare_test_packets(
mixnode_identity,
test_nonce,
num_test_packets
));
let processed_receiver_clone = self.processed_receiver.clone();
let gateway_client_clone = Arc::clone(&self.gateway_client);
let tester_marker = TestMarker::new(Arc::clone(&self.test_in_progress));
// start doing async things (send packets and watch for anything coming back)
future_to_promise(async move {
test_mixnode(
test_packets,
gateway_client_clone,
processed_receiver_clone,
tester_marker,
timeout,
)
.await
.into_promise_result()
})
}
}
-267
View File
@@ -1,267 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_client_core::config::GatewayEndpointConfig;
use nym_crypto::asymmetric::{encryption, identity};
use nym_topology::gateway::GatewayConversionError;
use nym_topology::mix::{Layer, MixnodeConversionError};
use nym_topology::{gateway, mix, MixLayer, NymTopology};
use nym_validator_client::client::{IdentityKeyRef, MixId};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use thiserror::Error;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
use wasm_utils::{console_log, simple_js_error};
#[derive(Debug, Error)]
pub enum WasmTopologyError {
#[error("got invalid mix layer {value}. Expected 1, 2 or 3.")]
InvalidMixLayer { value: u8 },
#[error(transparent)]
GatewayConversion(#[from] GatewayConversionError),
#[error(transparent)]
MixnodeConversion(#[from] MixnodeConversionError),
#[error("The provided mixnode map was malformed: {source}")]
MalformedMixnodeMap { source: serde_wasm_bindgen::Error },
#[error("The provided gateway list was malformed: {source}")]
MalformedGatewayList { source: serde_wasm_bindgen::Error },
}
impl From<WasmTopologyError> for JsValue {
fn from(value: WasmTopologyError) -> Self {
simple_js_error(value.to_string())
}
}
#[wasm_bindgen]
#[derive(Debug)]
pub struct WasmNymTopology {
inner: NymTopology,
}
#[wasm_bindgen]
impl WasmNymTopology {
#[wasm_bindgen(constructor)]
pub fn new(
// expected: BTreeMap<MixLayer, Vec<WasmMixNode>>,
// HashMap<MixLayer, Vec<WasmMixNode>> will also work because it has the same json representation
mixnodes: JsValue,
// expected: Vec<WasmGateway>
gateways: JsValue,
) -> Result<WasmNymTopology, WasmTopologyError> {
let mixnodes: BTreeMap<MixLayer, Vec<WasmMixNode>> =
serde_wasm_bindgen::from_value(mixnodes)
.map_err(|source| WasmTopologyError::MalformedMixnodeMap { source })?;
let gateways: Vec<WasmGateway> = serde_wasm_bindgen::from_value(gateways)
.map_err(|source| WasmTopologyError::MalformedGatewayList { source })?;
let mut converted_mixes = BTreeMap::new();
for (layer, nodes) in mixnodes {
let layer_nodes = nodes
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?;
converted_mixes.insert(layer, layer_nodes);
}
let gateways = gateways
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?;
Ok(WasmNymTopology {
inner: NymTopology::new(converted_mixes, gateways),
})
}
#[allow(dead_code)]
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
.gateways()
.iter()
.any(|g| g.identity_key.to_base58_string() == gateway_id)
}
pub fn print(&self) {
if !self.inner.mixes().is_empty() {
console_log!("mixnodes:");
for (layer, nodes) in self.inner.mixes() {
console_log!("\tlayer {layer}:");
for node in nodes {
console_log!("\t\t{} - {}", node.mix_id, node.identity_key)
}
}
} else {
console_log!("NO MIXNODES")
}
if !self.inner.gateways().is_empty() {
console_log!("gateways:");
for gateway in self.inner.gateways() {
console_log!("\t{}", gateway.identity_key)
}
} else {
console_log!("NO GATEWAYS")
}
}
}
impl From<WasmNymTopology> for NymTopology {
fn from(value: WasmNymTopology) -> Self {
value.inner
}
}
impl From<NymTopology> for WasmNymTopology {
fn from(value: NymTopology) -> Self {
WasmNymTopology { inner: value }
}
}
#[wasm_bindgen]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct WasmMixNode {
pub mix_id: MixId,
#[wasm_bindgen(getter_with_clone)]
pub owner: String,
#[wasm_bindgen(getter_with_clone)]
pub host: String,
pub mix_port: u16,
#[wasm_bindgen(getter_with_clone)]
pub identity_key: String,
#[wasm_bindgen(getter_with_clone)]
pub sphinx_key: String,
pub layer: MixLayer,
#[wasm_bindgen(getter_with_clone)]
pub version: String,
}
#[wasm_bindgen]
impl WasmMixNode {
#[wasm_bindgen(constructor)]
#[allow(clippy::too_many_arguments)]
pub fn new(
mix_id: MixId,
owner: String,
host: String,
mix_port: u16,
identity_key: String,
sphinx_key: String,
layer: MixLayer,
version: String,
) -> Self {
Self {
mix_id,
owner,
host,
mix_port,
identity_key,
sphinx_key,
layer,
version,
}
}
}
impl TryFrom<WasmMixNode> for mix::Node {
type Error = WasmTopologyError;
fn try_from(value: WasmMixNode) -> Result<Self, Self::Error> {
let host = mix::Node::parse_host(&value.host)?;
// try to completely resolve the host in the mix situation to avoid doing it every
// single time we want to construct a path
let mix_host = mix::Node::extract_mix_host(&host, value.mix_port)?;
Ok(mix::Node {
mix_id: value.mix_id,
owner: value.owner,
host,
mix_host,
identity_key: identity::PublicKey::from_base58_string(&value.identity_key)
.map_err(MixnodeConversionError::from)?,
sphinx_key: encryption::PublicKey::from_base58_string(&value.sphinx_key)
.map_err(MixnodeConversionError::from)?,
layer: Layer::try_from(value.layer)
.map_err(|_| WasmTopologyError::InvalidMixLayer { value: value.layer })?,
version: value.version,
})
}
}
#[wasm_bindgen]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct WasmGateway {
#[wasm_bindgen(getter_with_clone)]
pub owner: String,
#[wasm_bindgen(getter_with_clone)]
pub host: String,
pub mix_port: u16,
pub clients_port: u16,
#[wasm_bindgen(getter_with_clone)]
pub identity_key: String,
#[wasm_bindgen(getter_with_clone)]
pub sphinx_key: String,
#[wasm_bindgen(getter_with_clone)]
pub version: String,
}
#[wasm_bindgen]
impl WasmGateway {
#[wasm_bindgen(constructor)]
pub fn new(
owner: String,
host: String,
mix_port: u16,
clients_port: u16,
identity_key: String,
sphinx_key: String,
version: String,
) -> Self {
Self {
owner,
host,
mix_port,
clients_port,
identity_key,
sphinx_key,
version,
}
}
}
impl TryFrom<WasmGateway> for gateway::Node {
type Error = WasmTopologyError;
fn try_from(value: WasmGateway) -> Result<Self, Self::Error> {
let host = gateway::Node::parse_host(&value.host)?;
// try to completely resolve the host in the mix situation to avoid doing it every
// single time we want to construct a path
let mix_host = gateway::Node::extract_mix_host(&host, value.mix_port)?;
Ok(gateway::Node {
owner: value.owner,
host,
mix_host,
clients_port: value.clients_port,
identity_key: identity::PublicKey::from_base58_string(&value.identity_key)
.map_err(GatewayConversionError::from)?,
sphinx_key: encryption::PublicKey::from_base58_string(&value.sphinx_key)
.map_err(GatewayConversionError::from)?,
version: value.version,
})
}
}

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