Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea271d720d |
@@ -83,7 +83,7 @@ jobs:
|
||||
run: cargo install --version 0.112.0 wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make contracts-wasm
|
||||
run: make wasm
|
||||
|
||||
- name: Prepare build output
|
||||
shell: bash
|
||||
|
||||
@@ -87,14 +87,9 @@ jobs:
|
||||
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
||||
working-directory: dist/docs
|
||||
|
||||
- name: Build Project Artifacts (preview)
|
||||
if: github.ref != 'refs/heads/master'
|
||||
- name: Build Project Artifacts
|
||||
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
|
||||
working-directory: dist/docs
|
||||
- name: Build Project Artifacts (production)
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
|
||||
working-directory: dist/docs
|
||||
|
||||
- name: Deploy Project Artifacts to Vercel (preview)
|
||||
if: github.ref != 'refs/heads/master'
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
run: cargo install --version 0.112.0 wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make contracts-wasm
|
||||
run: make wasm
|
||||
|
||||
- name: Upload Mixnet Contract Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
name: Nightly builds on latest release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '14 2 * * *'
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from nightly_build_matrix_includes.json
|
||||
- uses: actions/checkout@v3
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
|
||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||
get_release:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: matrix_prep
|
||||
outputs:
|
||||
output1: ${{ steps.step2.outputs.latest_release }}
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: Fetch all branches
|
||||
run: git fetch --all
|
||||
- name: Set output variable to latest release branch
|
||||
id: step2
|
||||
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 | sed 's/ origin\///')" >> $GITHUB_OUTPUT
|
||||
build:
|
||||
needs: [get_release,matrix_prep]
|
||||
strategy:
|
||||
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
continue-on-error: true
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
|
||||
- name: Check out latest release branch
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{needs.get_release.outputs.output1}}
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run expensive tests
|
||||
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace -- --ignored
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
# nym-wallet (the rust part)
|
||||
- name: Build nym-wallet rust code
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
- name: Run nym-wallet tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
- name: Check nym-wallet formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
|
||||
|
||||
- name: Run clippy for nym-wallet
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
|
||||
|
||||
notification:
|
||||
needs: [build,get_release]
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Matrix - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "Nym nightly build on latest release"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH_NAME: "${{needs.get_release.outputs.output1}}"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -0,0 +1,191 @@
|
||||
name: Nightly builds on second latest release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '24 2 * * *'
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from nightly_build_matrix_includes.json
|
||||
- uses: actions/checkout@v3
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
|
||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||
get_release:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: matrix_prep
|
||||
outputs:
|
||||
output1: ${{ steps.step2.outputs.latest_release }}
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: Fetch all branches
|
||||
run: git fetch --all
|
||||
- name: Set output variable to latest release branch
|
||||
id: step2
|
||||
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 2 | head -n 1 | sed 's/ origin\///')" >> $GITHUB_OUTPUT
|
||||
build:
|
||||
needs: [get_release,matrix_prep]
|
||||
strategy:
|
||||
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
||||
continue-on-error: true
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
|
||||
- name: Check out latest release branch
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{needs.get_release.outputs.output1}}
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run expensive tests
|
||||
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace -- --ignored
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
# nym-wallet (the rust part)
|
||||
- name: Build nym-wallet rust code
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
- name: Run nym-wallet tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
- name: Check nym-wallet formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
|
||||
|
||||
- name: Run clippy for nym-wallet
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
|
||||
|
||||
notification:
|
||||
needs: [build,get_release]
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Matrix - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "Nym nightly build on latest release"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH_NAME: "${{needs.get_release.outputs.output1}}"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -4,11 +4,9 @@ on:
|
||||
push:
|
||||
paths:
|
||||
- "sdk/typescript/**"
|
||||
- "wasm/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "sdk/typescript/**"
|
||||
- "wasm/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -28,39 +26,17 @@ jobs:
|
||||
toolchain: stable
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Build branch WASM packages
|
||||
run: make sdk-wasm-build
|
||||
|
||||
- name: Install
|
||||
run: yarn
|
||||
- name: Build
|
||||
run: yarn docs:prod:build
|
||||
- name: Deploy branch to CI www (docs)
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "dist/ts/"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/sdk-ts-docs-${{ env.GITHUB_REF_SLUG }}
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: ts-packages
|
||||
NYM_PROJECT_NAME: "sdk-ts-docs"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}/docs/sdk/typescript"
|
||||
NYM_CI_WWW_LOCATION: "sdk-ts-docs-${{ env.GITHUB_REF_SLUG }}"
|
||||
NYM_PROJECT_NAME: "ts-packages"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
|
||||
@@ -25,37 +25,27 @@ jobs:
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
continue-on-error: true
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Install
|
||||
run: yarn
|
||||
|
||||
- name: Build packages
|
||||
run: yarn build:ci:sdk
|
||||
|
||||
run: yarn build
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
- name: Typecheck with tsc
|
||||
run: yarn tsc
|
||||
|
||||
run: yarn lint && yarn tsc
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Wasm Client
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'clients/webassembly/**'
|
||||
- 'clients/client-core/**'
|
||||
- 'common/**'
|
||||
- 'contracts/**'
|
||||
- 'gateway/gateway-requests/**'
|
||||
- 'nym-api/nym-api-requests/**'
|
||||
|
||||
jobs:
|
||||
wasm:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path clients/webassembly/Cargo.toml -- --check
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
||||
@@ -1,46 +0,0 @@
|
||||
name: Wasm Client
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'wasm/**'
|
||||
- 'clients/client-core/**'
|
||||
- 'common/**'
|
||||
|
||||
jobs:
|
||||
wasm:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Install wasm-bindgen-cli
|
||||
run: cargo install wasm-bindgen-cli
|
||||
|
||||
- name: "Build"
|
||||
run: make sdk-wasm-build
|
||||
|
||||
- name: "Test"
|
||||
run: make sdk-wasm-test
|
||||
|
||||
- name: "Lint"
|
||||
run: make sdk-wasm-lint
|
||||
+1
-3
@@ -43,6 +43,4 @@ envs/qwerty.env
|
||||
.parcel-cache
|
||||
**/.DS_Store
|
||||
cpu-cycles/libcpucycles/build
|
||||
foxyfox.env
|
||||
|
||||
.next
|
||||
foxyfox.env
|
||||
@@ -4,22 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v1.1.31-kitkat] (2023-09-12)
|
||||
|
||||
- feat: add name to `TaskClient` ([#3844])
|
||||
- added 'open_proxy', 'enabled_statistics' and 'statistics_recipient' to NR config ([#3839])
|
||||
- MixFetch: initial prototype for insecure HTTP ([#3645])
|
||||
- MixFetch: prototype implementing TLS in WASM for HTTPS ([#3644])
|
||||
- SDK: build package for NodeJS ([#3558])
|
||||
- [Issue] There is already an open connection to this client ([#2845])
|
||||
|
||||
[#3844]: https://github.com/nymtech/nym/pull/3844
|
||||
[#3839]: https://github.com/nymtech/nym/pull/3839
|
||||
[#3645]: https://github.com/nymtech/nym/issues/3645
|
||||
[#3644]: https://github.com/nymtech/nym/issues/3644
|
||||
[#3558]: https://github.com/nymtech/nym/issues/3558
|
||||
[#2845]: https://github.com/nymtech/nym/issues/2845
|
||||
|
||||
## [v1.1.30-twix] (2023-09-05)
|
||||
|
||||
- geo_aware_provider: fix too much filtering of gateways ([#3826])
|
||||
|
||||
Generated
+86
-832
File diff suppressed because it is too large
Load Diff
+4
-22
@@ -46,7 +46,6 @@ members = [
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/execute",
|
||||
"common/http-requests",
|
||||
"common/inclusion-probability",
|
||||
"common/ledger",
|
||||
"common/mixnode-common",
|
||||
@@ -74,10 +73,7 @@ members = [
|
||||
"common/task",
|
||||
"common/topology",
|
||||
"common/types",
|
||||
"common/wasm/client-core",
|
||||
"common/wasm/storage",
|
||||
"common/wasm/utils",
|
||||
"common/wireguard",
|
||||
"common/wasm-utils",
|
||||
"explorer-api",
|
||||
"explorer-api/explorer-api-requests",
|
||||
"explorer-api/explorer-client",
|
||||
@@ -91,18 +87,11 @@ members = [
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-outfox",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/sdk-version-bump",
|
||||
"tools/nym-cli",
|
||||
"tools/nym-nr-query",
|
||||
"tools/ts-rs-cli",
|
||||
"wasm/client",
|
||||
"wasm/full-nym-wasm",
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"tools/ts-rs-cli"
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -116,7 +105,7 @@ default-members = [
|
||||
"explorer-api",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "cpu-cycles"]
|
||||
exclude = ["explorer", "contracts", "clients/webassembly", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-browser-extension/storage", "cpu-cycles"]
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Nym Technologies SA"]
|
||||
@@ -128,7 +117,7 @@ license = "Apache-2.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.71"
|
||||
async-trait = "0.1.68"
|
||||
async-trait = "0.1.64"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.3.0"
|
||||
@@ -167,11 +156,4 @@ url = "2.4"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
# wasm-related dependencies
|
||||
gloo-utils = "0.1.7"
|
||||
js-sys = "0.3.63"
|
||||
serde-wasm-bindgen = "0.5.0"
|
||||
tsify = "0.4.5"
|
||||
wasm-bindgen = "0.2.86"
|
||||
wasm-bindgen-futures = "0.4.37"
|
||||
wasmtimer = "0.2.0"
|
||||
web-sys = "0.3.63"
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
# Default target
|
||||
all: test
|
||||
|
||||
test: clippy-all cargo-test contracts-wasm sdk-wasm-test fmt
|
||||
test: clippy-all cargo-test wasm fmt
|
||||
|
||||
test-all: test cargo-test-expensive
|
||||
|
||||
no-clippy: build cargo-test contracts-wasm fmt fmt-browser-extension-storage
|
||||
no-clippy: build cargo-test wasm fmt
|
||||
|
||||
happy: fmt clippy-happy test
|
||||
|
||||
build: sdk-wasm-build build-browser-extension-storage
|
||||
|
||||
# Building release binaries is a little manual as we can't just build --release
|
||||
# on all workspaces.
|
||||
build-release: build-release-main contracts-wasm
|
||||
|
||||
clippy: sdk-wasm-lint clippy-browser-extension-storage
|
||||
build-release: build-release-main wasm
|
||||
|
||||
# Deprecated
|
||||
# For backwards compatibility
|
||||
@@ -47,9 +43,6 @@ test-$(1):
|
||||
test-expensive-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
|
||||
|
||||
build-standalone-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml $(3)
|
||||
|
||||
build-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||
|
||||
@@ -81,7 +74,7 @@ endef
|
||||
|
||||
$(eval $(call add_cargo_workspace,main,.))
|
||||
$(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unknown-unknown))
|
||||
#$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown))
|
||||
$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown))
|
||||
$(eval $(call add_cargo_workspace,wallet,nym-wallet,))
|
||||
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
|
||||
|
||||
@@ -95,67 +88,6 @@ build-explorer-api:
|
||||
build-nym-cli:
|
||||
cargo build -p nym-cli --release
|
||||
|
||||
build-browser-extension-storage:
|
||||
cargo build -p extension-storage --target wasm32-unknown-unknown
|
||||
|
||||
fmt-browser-extension-storage:
|
||||
cargo fmt -p extension-storage -- --check
|
||||
|
||||
clippy-browser-extension-storage:
|
||||
cargo clippy -p extension-storage --target wasm32-unknown-unknown -- -Dwarnings
|
||||
|
||||
sdk-wasm: sdk-wasm-build sdk-wasm-test sdk-wasm-lint
|
||||
|
||||
sdk-wasm-build:
|
||||
# browser storage
|
||||
$(MAKE) -C nym-browser-extension/storage wasm-pack
|
||||
|
||||
# client
|
||||
$(MAKE) -C wasm/client build
|
||||
|
||||
# node-tester
|
||||
$(MAKE) -C wasm/node-tester build
|
||||
|
||||
# mix-fetch
|
||||
$(MAKE) -C wasm/mix-fetch build
|
||||
|
||||
# full
|
||||
$(MAKE) -C wasm/full-nym-wasm build-full
|
||||
|
||||
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
|
||||
sdk-typescript-build:
|
||||
lerna run --scope @nymproject/sdk build --stream
|
||||
lerna run --scope @nymproject/mix-fetch build --stream
|
||||
lerna run --scope @nymproject/node-tester build --stream
|
||||
|
||||
sdk-wasm-test:
|
||||
# # client
|
||||
# cargo test -p nym-client-wasm --target wasm32-unknown-unknown
|
||||
#
|
||||
# # node-tester
|
||||
# cargo test -p nym-node-tester-wasm --target wasm32-unknown-unknown
|
||||
#
|
||||
# # mix-fetch
|
||||
# #cargo test -p nym-wasm-sdk --target wasm32-unknown-unknown
|
||||
#
|
||||
# # full
|
||||
# cargo test -p nym-wasm-sdk --target wasm32-unknown-unknown
|
||||
|
||||
|
||||
sdk-wasm-lint:
|
||||
# client
|
||||
cargo clippy -p nym-client-wasm --target wasm32-unknown-unknown -- -Dwarnings
|
||||
|
||||
# node-tester
|
||||
cargo clippy -p nym-node-tester-wasm --target wasm32-unknown-unknown -- -Dwarnings
|
||||
|
||||
# mix-fetch
|
||||
$(MAKE) -C wasm/mix-fetch check-fmt
|
||||
|
||||
# full
|
||||
cargo clippy -p nym-wasm-sdk --target wasm32-unknown-unknown -- -Dwarnings
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build contracts ready for deploy
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -166,12 +98,12 @@ MIXNET_CONTRACT=$(CONTRACTS_OUT_DIR)/mixnet_contract.wasm
|
||||
SERVICE_PROVIDER_DIRECTORY_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_service_provider_directory.wasm
|
||||
NAME_SERVICE_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_name_service.wasm
|
||||
|
||||
contracts-wasm: contracts-wasm-build contracts-wasm-opt
|
||||
wasm: wasm-build wasm-opt
|
||||
|
||||
contracts-wasm-build:
|
||||
wasm-build:
|
||||
RUSTFLAGS='-C link-arg=-s' cargo build --lib --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
|
||||
|
||||
contracts-wasm-opt:
|
||||
wasm-opt:
|
||||
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 $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) -o $(SERVICE_PROVIDER_DIRECTORY_CONTRACT)
|
||||
@@ -185,7 +117,7 @@ contract-schema:
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# NOTE: this seems deprecated an not needed anymore?
|
||||
mixnet-opt: contracts-wasm
|
||||
mixnet-opt: wasm
|
||||
cd contracts/mixnet && make opt
|
||||
|
||||
generate-typescript:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.29"
|
||||
version = "1.1.28"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
client::config::{
|
||||
default_config_filepath, persistence::ClientPaths, Config, Socket, SocketType,
|
||||
},
|
||||
error::ClientError,
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -48,18 +43,18 @@ impl ConfigV1_1_20_2 {
|
||||
|
||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
||||
// so its returned to be stored elsewhere.
|
||||
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), ClientError> {
|
||||
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()?,
|
||||
common_paths: self.storage_paths.common_paths.upgrade_default(),
|
||||
},
|
||||
logging: self.logging,
|
||||
};
|
||||
|
||||
Ok((config, gateway_details))
|
||||
(config, gateway_details)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ use nym_task::connections::TransmissionLane;
|
||||
use nym_task::TaskManager;
|
||||
use nym_validator_client::QueryHttpRpcNyxdClient;
|
||||
use std::error::Error;
|
||||
use std::path::PathBuf;
|
||||
use tokio::sync::watch::error::SendError;
|
||||
|
||||
pub use nym_sphinx::addressing::clients::Recipient;
|
||||
@@ -35,17 +34,11 @@ pub struct SocketClient {
|
||||
/// Client configuration options, including, among other things, packet sending rates,
|
||||
/// key filepaths, etc.
|
||||
config: Config,
|
||||
|
||||
/// Optional path to a .json file containing standalone network details.
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl SocketClient {
|
||||
pub fn new(config: Config, custom_mixnet: Option<PathBuf>) -> Self {
|
||||
SocketClient {
|
||||
config,
|
||||
custom_mixnet,
|
||||
}
|
||||
pub fn new(config: Config) -> Self {
|
||||
SocketClient { config }
|
||||
}
|
||||
|
||||
fn start_websocket_listener(
|
||||
@@ -116,11 +109,7 @@ impl SocketClient {
|
||||
|
||||
let storage = self.initialise_storage().await?;
|
||||
|
||||
let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client);
|
||||
|
||||
if let Some(custom_mixnet) = &self.custom_mixnet {
|
||||
base_client = base_client.with_stored_topology(custom_mixnet)?;
|
||||
}
|
||||
let base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client);
|
||||
|
||||
Ok(base_client)
|
||||
}
|
||||
|
||||
@@ -15,15 +15,12 @@ use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use tap::TapFallible;
|
||||
|
||||
@@ -52,12 +49,7 @@ pub(crate) struct Init {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)]
|
||||
#[clap(long, alias = "api_validators", value_delimiter = ',')]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
@@ -73,10 +65,6 @@ pub(crate) struct Init {
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
@@ -142,7 +130,7 @@ fn init_paths(id: &str) -> io::Result<()> {
|
||||
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...");
|
||||
|
||||
let id = &args.id;
|
||||
@@ -185,25 +173,12 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
|
||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
|
||||
let network_gateways = if let Some(hardcoded_topology) = args
|
||||
.custom_mixnet
|
||||
.map(NymTopology::new_from_file)
|
||||
.transpose()?
|
||||
{
|
||||
// hardcoded_topology
|
||||
hardcoded_topology.get_gateways()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
current_gateways(&mut rng, &config.base.client.nym_api_urls).await?
|
||||
};
|
||||
|
||||
let init_details = nym_client_core::init::setup_gateway_from(
|
||||
let init_details = nym_client_core::init::setup_gateway(
|
||||
gateway_setup,
|
||||
&key_store,
|
||||
&details_store,
|
||||
register_gateway,
|
||||
Some(&network_gateways),
|
||||
Some(&config.base.client.nym_api_urls),
|
||||
)
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?
|
||||
|
||||
@@ -84,8 +84,8 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
|
||||
let bin_name = "nym-native-client";
|
||||
|
||||
match args.command {
|
||||
Commands::Init(m) => init::execute(m).await?,
|
||||
Commands::Run(m) => run::execute(m).await?,
|
||||
Commands::Init(m) => init::execute(&m).await?,
|
||||
Commands::Run(m) => run::execute(&m).await?,
|
||||
Commands::BuildInfo(m) => build_info::execute(m),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
|
||||
@@ -157,7 +157,7 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
|
||||
|
||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
||||
let (updated, gateway_config) = updated_step2.upgrade()?;
|
||||
let (updated, gateway_config) = updated_step2.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
@@ -177,7 +177,7 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
|
||||
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()?;
|
||||
let (updated, gateway_config) = updated_step1.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
@@ -194,7 +194,7 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
|
||||
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()?;
|
||||
let (updated, gateway_config) = old_config.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
|
||||
@@ -13,7 +13,6 @@ use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Run {
|
||||
@@ -26,12 +25,7 @@ pub(crate) struct Run {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)]
|
||||
#[clap(long, alias = "api_validators", value_delimiter = ',')]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
@@ -52,10 +46,6 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
@@ -105,7 +95,7 @@ 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 mut config = try_load_current_config(&args.id)?;
|
||||
@@ -116,7 +106,5 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync
|
||||
return Err(Box::new(ClientError::FailedLocalVersionCheck));
|
||||
}
|
||||
|
||||
SocketClient::new(config, args.custom_mixnet)
|
||||
.run_socket_forever()
|
||||
.await
|
||||
SocketClient::new(config).run_socket_forever().await
|
||||
}
|
||||
|
||||
@@ -230,7 +230,8 @@ impl ServerResponse {
|
||||
|
||||
let error_kind = ErrorKind::try_from(b[1])?;
|
||||
|
||||
let message_len = u64::from_be_bytes(b[2..2 + size_of::<u64>()].try_into().unwrap());
|
||||
let message_len =
|
||||
u64::from_be_bytes(b[2..2 + size_of::<u64>()].as_ref().try_into().unwrap());
|
||||
let message = &b[2 + size_of::<u64>()..];
|
||||
if message.len() as u64 != message_len {
|
||||
return Err(error::Error::new(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.29"
|
||||
version = "1.1.28"
|
||||
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"
|
||||
edition = "2021"
|
||||
@@ -16,7 +16,6 @@ serde_json = { workspace = true }
|
||||
tap = "1.0.1"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
rand = "0.7.3"
|
||||
url = { workspace = true }
|
||||
|
||||
# internal
|
||||
|
||||
@@ -14,14 +14,11 @@ use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use tap::TapFallible;
|
||||
|
||||
@@ -71,10 +68,6 @@ pub(crate) struct Init {
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
@@ -145,7 +138,7 @@ fn init_paths(id: &str) -> io::Result<()> {
|
||||
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...");
|
||||
|
||||
let id = &args.id;
|
||||
@@ -192,25 +185,12 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
|
||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
|
||||
let network_gateways = if let Some(hardcoded_topology) = args
|
||||
.custom_mixnet
|
||||
.map(NymTopology::new_from_file)
|
||||
.transpose()?
|
||||
{
|
||||
// hardcoded_topology
|
||||
hardcoded_topology.get_gateways()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
current_gateways(&mut rng, &config.core.base.client.nym_api_urls).await?
|
||||
};
|
||||
|
||||
let init_details = nym_client_core::init::setup_gateway_from(
|
||||
let init_details = nym_client_core::init::setup_gateway(
|
||||
gateway_setup,
|
||||
&key_store,
|
||||
&details_store,
|
||||
register_gateway,
|
||||
Some(&network_gateways),
|
||||
Some(&config.core.base.client.nym_api_urls),
|
||||
)
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?
|
||||
|
||||
@@ -87,8 +87,8 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
|
||||
let bin_name = "nym-socks5-client";
|
||||
|
||||
match args.command {
|
||||
Commands::Init(m) => init::execute(m).await?,
|
||||
Commands::Run(m) => run::execute(m).await?,
|
||||
Commands::Init(m) => init::execute(&m).await?,
|
||||
Commands::Run(m) => run::execute(&m).await?,
|
||||
Commands::BuildInfo(m) => build_info::execute(m),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
|
||||
@@ -199,7 +199,7 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
|
||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
||||
let (updated, gateway_config) = updated_step2.upgrade()?;
|
||||
let (updated, gateway_config) = updated_step2.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
@@ -219,7 +219,7 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_20_2 = old_config.into();
|
||||
let (updated, gateway_config) = updated_step1.upgrade()?;
|
||||
let (updated, gateway_config) = updated_step1.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
@@ -236,7 +236,7 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
info!("It seems the client is using <= v1.1.20_2 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let (updated, gateway_config) = old_config.upgrade()?;
|
||||
let (updated, gateway_config) = old_config.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
|
||||
@@ -15,7 +15,6 @@ use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_socks5_client_core::NymClient;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Run {
|
||||
@@ -46,17 +45,13 @@ pub(crate) struct Run {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the Nym APIs
|
||||
#[clap(long, value_delimiter = ',', group = "network")]
|
||||
#[clap(long, value_delimiter = ',')]
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", group = "routing", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hide = true)]
|
||||
@@ -67,7 +62,7 @@ pub(crate) struct Run {
|
||||
no_cover: bool,
|
||||
|
||||
/// Set geo-aware mixnode selection when sending mixnet traffic, for experiments only.
|
||||
#[clap(long, hide = true, value_parser = validate_country_group, group="routing")]
|
||||
#[clap(long, hide = true, value_parser = validate_country_group)]
|
||||
geo_routing: Option<CountryGroup>,
|
||||
|
||||
/// Enable medium mixnet traffic, for experiments only.
|
||||
@@ -129,7 +124,7 @@ 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 mut config = try_load_current_config(&args.id)?;
|
||||
@@ -143,7 +138,5 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error +
|
||||
let storage =
|
||||
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
|
||||
.await?;
|
||||
NymClient::new(config.core, storage, args.custom_mixnet)
|
||||
.run_forever()
|
||||
.await
|
||||
NymClient::new(config.core, storage).run_forever().await
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
config::{default_config_filepath, persistence::SocksClientPaths, Config},
|
||||
error::Socks5ClientError,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
pub use nym_socks5_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as CoreConfigV1_1_20_2;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
||||
pub struct SocksClientPathsV1_1_20_2 {
|
||||
#[serde(flatten)]
|
||||
@@ -43,16 +39,16 @@ impl ConfigV1_1_20_2 {
|
||||
|
||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
||||
// so its returned to be stored elsewhere.
|
||||
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), Socks5ClientError> {
|
||||
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()?,
|
||||
common_paths: self.storage_paths.common_paths.upgrade_default(),
|
||||
},
|
||||
logging: self.logging,
|
||||
};
|
||||
|
||||
Ok((config, gateway_details))
|
||||
(config, gateway_details)
|
||||
}
|
||||
}
|
||||
|
||||
+24
-24
@@ -1,31 +1,33 @@
|
||||
{
|
||||
"name": "@nymproject/nym-validator-client",
|
||||
"version": "1.2.0-rc.2",
|
||||
"version": "0.19.0",
|
||||
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/nymtech/nym",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "rollup -c ./rollup.config.mjs",
|
||||
"build:types": "rollup-type-bundler --dist ./dist",
|
||||
"build:prod": "sh ./scripts/build-prod.sh",
|
||||
"test": "ts-mocha -p ./tsconfig.test.json ./src/tests/**/*.test.ts",
|
||||
"testmock": "ts-mocha -p ./tsconfig.test.json ./src/tests/mock/*.test.ts",
|
||||
"coverage": "nyc npm test",
|
||||
"clean": "rm -rf ./dist",
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix",
|
||||
"lint:tsc": "tsc --noEmit",
|
||||
"docs": "typedoc --out docs src/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"./dist/*"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "Nym Technologies SA (https://nymtech.net)",
|
||||
"contributors": [
|
||||
"Dave Hrycyszyn (https://constructiveproof.com/)",
|
||||
"Jędrzej Stuczynski"
|
||||
],
|
||||
"files": [
|
||||
"./dist/*"
|
||||
],
|
||||
"main": "dist/index.js",
|
||||
"repository": "https://github.com/nymtech/nym",
|
||||
"scripts": {
|
||||
"build": "rollup -c ./rollup.config.mjs",
|
||||
"build:prod": "sh ./scripts/build-prod.sh",
|
||||
"build:types": "rollup-type-bundler --dist ./dist",
|
||||
"clean": "rm -rf ./dist",
|
||||
"coverage": "nyc npm test",
|
||||
"docs": "typedoc --out docs src/index.ts",
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix",
|
||||
"test": "ts-mocha -p ./tsconfig.test.json ./src/tests/**/*.test.ts",
|
||||
"testmock": "ts-mocha -p ./tsconfig.test.json ./src/tests/mock/*.test.ts",
|
||||
"tsc": "tsc --noEmit"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@cosmjs/cosmwasm-stargate": "^0.29.5",
|
||||
"@cosmjs/crypto": "^0.29.5",
|
||||
@@ -62,7 +64,5 @@
|
||||
"ts-mocha": "^10.0.0",
|
||||
"typedoc": "^0.22.13",
|
||||
"typescript": "^4.6.2"
|
||||
},
|
||||
"private": false,
|
||||
"types": "dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
install:
|
||||
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --locked
|
||||
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
||||
@@ -0,0 +1,6 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
node_modules
|
||||
@@ -0,0 +1,69 @@
|
||||
language: rust
|
||||
sudo: false
|
||||
|
||||
cache: cargo
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
||||
# Builds with wasm-pack.
|
||||
- rust: beta
|
||||
env: RUST_BACKTRACE=1
|
||||
addons:
|
||||
firefox: latest
|
||||
chrome: stable
|
||||
before_script:
|
||||
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
|
||||
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
|
||||
- cargo install-update -a
|
||||
- curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
|
||||
script:
|
||||
- cargo generate --git . --name testing
|
||||
# Having a broken Cargo.toml (in that it has curlies in fields) anywhere
|
||||
# in any of our parent dirs is problematic.
|
||||
- mv Cargo.toml Cargo.toml.tmpl
|
||||
- cd testing
|
||||
- wasm-pack build
|
||||
- wasm-pack test --chrome --firefox --headless
|
||||
|
||||
# Builds on nightly.
|
||||
- rust: nightly
|
||||
env: RUST_BACKTRACE=1
|
||||
before_script:
|
||||
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
|
||||
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
|
||||
- cargo install-update -a
|
||||
- rustup target add wasm32-unknown-unknown
|
||||
script:
|
||||
- cargo generate --git . --name testing
|
||||
- mv Cargo.toml Cargo.toml.tmpl
|
||||
- cd testing
|
||||
- cargo check
|
||||
- cargo check --target wasm32-unknown-unknown
|
||||
- cargo check --no-default-features
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features
|
||||
- cargo check --no-default-features --features console_error_panic_hook
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook
|
||||
- cargo check --no-default-features --features "console_error_panic_hook wee_alloc"
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc"
|
||||
|
||||
# Builds on beta.
|
||||
- rust: beta
|
||||
env: RUST_BACKTRACE=1
|
||||
before_script:
|
||||
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
|
||||
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
|
||||
- cargo install-update -a
|
||||
- rustup target add wasm32-unknown-unknown
|
||||
script:
|
||||
- cargo generate --git . --name testing
|
||||
- mv Cargo.toml Cargo.toml.tmpl
|
||||
- cd testing
|
||||
- cargo check
|
||||
- cargo check --target wasm32-unknown-unknown
|
||||
- cargo check --no-default-features
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features
|
||||
- cargo check --no-default-features --features console_error_panic_hook
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook
|
||||
# Note: no enabling the `wee_alloc` feature here because it requires
|
||||
# nightly for now.
|
||||
Generated
+5559
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
[package]
|
||||
name = "nym-client-wasm"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
description = "A webassembly client which can be used to interact with the the Nym privacy platform. Wasm is used for Sphinx packet generation."
|
||||
rust-version = "1.56"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
offline-test = []
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.68"
|
||||
bs58 = "0.4.0"
|
||||
futures = "0.3"
|
||||
js-sys = "0.3"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
anyhow = "1.0"
|
||||
serde-wasm-bindgen = "0.5"
|
||||
tokio = { version = "1.24.1", features = ["sync"] }
|
||||
url = "2.2"
|
||||
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = "0.4"
|
||||
thiserror = "1.0.40"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
wasmtimer = { version = "0.2.0", features = ["tokio"] }
|
||||
|
||||
# internal
|
||||
nym-node-tester-utils = { path = "../../common/node-tester-utils" }
|
||||
nym-client-core = { path = "../../common/client-core", default-features = false, features = ["wasm"] }
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "serde"] }
|
||||
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-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
|
||||
# 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
|
||||
# code size when deploying.
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
|
||||
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
|
||||
# compared to the default allocator's ~10K. It is slower than the default
|
||||
# allocator, however.
|
||||
#
|
||||
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
|
||||
wee_alloc = { version = "0.4", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = false
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 'z'
|
||||
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,8 @@
|
||||
clippy:
|
||||
cargo clippy --all-features --target wasm32-unknown-unknown -- -D warnings
|
||||
|
||||
test:
|
||||
wasm-pack test --node
|
||||
|
||||
wasm-build:
|
||||
wasm-pack build
|
||||
@@ -20,20 +20,18 @@ See the [SDK directory](../../sdk/typescript/examples) for examples on how to us
|
||||
|
||||
## Developing
|
||||
|
||||
Make sure you have `wasm-pack` installed (`cargo install wasm-pack`) before you start.
|
||||
This is a Rust crate which is set up to automatically cross-compile the contents of `src` to WebAssembly (aka wasm). It's published from the main [Nym platform monorepo](https://github.com/nymtech/nym) in the `clients/webassembly` directory.
|
||||
|
||||
Please use the [Makefile](Makefile) targets to build this crate and compile it to WASM:
|
||||
First, make sure you've got all the [Rust wasm toolchain](https://rustwasm.github.io/docs/book/) installed. Cross-compilation sounds scary, but the Rust crew have enabled a remarkably simple setup.
|
||||
|
||||
```
|
||||
make build
|
||||
```
|
||||
Furthermore, [wasm-bindgen documentation](https://rustwasm.github.io/docs/wasm-bindgen/) provides excellent tips to solving common problems.
|
||||
|
||||
The outputs will be found [here](../../dist/wasm/client) where it will be accessible to the rest of the monorepo using workspaces.
|
||||
Whenever you change any Rust in the `src` directory, run `wasm-pack build --scope nymproject` to update the built wasm artefact in the `pkg` directory.
|
||||
|
||||
To be clear, this is not something that most JS developers need to worry about, this is only for Nym devs. The packages on NPM have all files in place. Just install and enjoy!
|
||||
|
||||
### Packaging
|
||||
|
||||
> **TODO**
|
||||
|
||||
If you're a Nym platform developer who's made changes to the Rust files and wants to re-publish the package to NPM, here's how you do it:
|
||||
|
||||
1. bump version numbers as necessary for SemVer
|
||||
@@ -5,7 +5,6 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nym WebAssembly Demo</title>
|
||||
<script src="bootstrap.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -24,8 +23,8 @@
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<label>Magic Payload: </label>
|
||||
<input type="text" size = "60" id="magic_payload" value="...">
|
||||
<label>Mixnode Identity: </label>
|
||||
<input type="text" size = "60" id="mixnode_identity" value="...">
|
||||
<button id="magic-button">✨ Magic Test Button ✨</button>
|
||||
</div>
|
||||
|
||||
@@ -38,7 +37,7 @@
|
||||
<p>
|
||||
<span id="output"></span>
|
||||
</p>
|
||||
|
||||
<script src="./bootstrap.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -29,10 +29,6 @@ class WebWorkerClient {
|
||||
const {message, senderTag, isTestPacket } = ev.data.args;
|
||||
displayReceived(message, senderTag, isTestPacket);
|
||||
break;
|
||||
case 'DisplayString':
|
||||
const { rawString } = ev.data.args;
|
||||
displayReceivedRawString(rawString)
|
||||
break;
|
||||
case 'DisableMagicTestButton':
|
||||
const magicButton = document.querySelector('#magic-button');
|
||||
magicButton.setAttribute('disabled', "true")
|
||||
@@ -61,14 +57,14 @@ class WebWorkerClient {
|
||||
});
|
||||
};
|
||||
|
||||
sendMagicPayload = (mixnodeIdentity) => {
|
||||
sendTestPacket = (mixnodeIdentity) => {
|
||||
if (!this.worker) {
|
||||
console.error('Could not send message because worker does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
this.worker.postMessage({
|
||||
kind: 'MagicPayload',
|
||||
kind: 'TestPacket',
|
||||
args: {
|
||||
mixnodeIdentity,
|
||||
},
|
||||
@@ -88,7 +84,7 @@ async function main() {
|
||||
|
||||
const magicButton = document.querySelector('#magic-button');
|
||||
magicButton.onclick = function () {
|
||||
sendMagicPayload();
|
||||
sendTestPacket();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,11 +102,11 @@ async function sendMessageTo() {
|
||||
displaySend(message);
|
||||
}
|
||||
|
||||
async function sendMagicPayload() {
|
||||
const payload = document.getElementById('magic_payload').value;
|
||||
await client.sendMagicPayload(payload)
|
||||
async function sendTestPacket() {
|
||||
const mixnodeIdentity = document.getElementById('mixnode_identity').value;
|
||||
|
||||
displaySend(`clicked the button and the payload is: ${payload}...`);
|
||||
await client.sendTestPacket(mixnodeIdentity)
|
||||
displaySend(`sending test packets to: ${mixnodeIdentity}...`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,6 +146,7 @@ function displayReceived(raw, sender_tag, isTestPacket) {
|
||||
displayReceivedRawString(content)
|
||||
}
|
||||
|
||||
|
||||
function displayReceivedRawString(raw) {
|
||||
let timestamp = new Date().toISOString().substr(11, 12);
|
||||
let receivedDiv = document.createElement('div');
|
||||
+2
-2
@@ -28,13 +28,13 @@
|
||||
},
|
||||
"homepage": "https://nymtech.net/docs",
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"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:../../../dist/wasm/client"
|
||||
"@nymproject/nym-client-wasm": "file:../pkg"
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -15,14 +15,14 @@ module.exports = {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: '[name].js',
|
||||
},
|
||||
mode: 'development',
|
||||
// mode: 'production',
|
||||
// mode: 'development',
|
||||
mode: 'production',
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
'index.html',
|
||||
{
|
||||
from: '../../../dist/wasm/client/*.(js|wasm)',
|
||||
from: 'node_modules/@nymproject/nym-client-wasm/*.(js|wasm)',
|
||||
to: '[name][ext]',
|
||||
},
|
||||
],
|
||||
@@ -0,0 +1,382 @@
|
||||
// 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();
|
||||
@@ -74,7 +74,7 @@
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@nymproject/nym-client-wasm@file:../pkg":
|
||||
version "1.1.1"
|
||||
version "1.1.0"
|
||||
|
||||
"@types/body-parser@*":
|
||||
version "1.19.2"
|
||||
@@ -1028,9 +1028,6 @@ globby@^12.0.2:
|
||||
merge2 "^1.4.1"
|
||||
slash "^4.0.0"
|
||||
|
||||
"go-mix-conn@file:../go-mix-conn/build":
|
||||
version "0.0.0"
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
+323
-410
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
|
||||
@@ -6,90 +6,342 @@
|
||||
// another issue due to #[wasm_bindgen] and `Copy` trait
|
||||
#![allow(dropping_copy_types)]
|
||||
|
||||
use crate::error::WasmCoreError;
|
||||
use nym_config::helpers::OptionalSet;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub mod r#override;
|
||||
|
||||
pub use nym_client_core::config::{
|
||||
use nym_client_core::config::{
|
||||
Acknowledgements as ConfigAcknowledgements, Config as BaseClientConfig,
|
||||
CoverTraffic as ConfigCoverTraffic, DebugConfig as ConfigDebug,
|
||||
GatewayConnection as ConfigGatewayConnection, ReplySurbs as ConfigReplySurbs,
|
||||
Topology as ConfigTopology, Traffic as ConfigTraffic,
|
||||
};
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub fn new_base_client_config(
|
||||
id: String,
|
||||
version: String,
|
||||
nym_api: Option<String>,
|
||||
nyxd: Option<String>,
|
||||
debug: Option<DebugWasm>,
|
||||
) -> Result<BaseClientConfig, WasmCoreError> {
|
||||
let nym_api_url = match nym_api {
|
||||
Some(raw) => Some(
|
||||
raw.parse()
|
||||
.map_err(|source| WasmCoreError::MalformedUrl { raw, source })?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let nyxd_url = match nyxd {
|
||||
Some(raw) => Some(
|
||||
raw.parse()
|
||||
.map_err(|source| WasmCoreError::MalformedUrl { raw, source })?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(BaseClientConfig::new(id, version)
|
||||
.with_optional(
|
||||
BaseClientConfig::with_custom_nym_apis,
|
||||
nym_api_url.map(|u| vec![u]),
|
||||
)
|
||||
.with_optional(
|
||||
BaseClientConfig::with_custom_nyxd,
|
||||
nyxd_url.map(|u| vec![u]),
|
||||
)
|
||||
.with_debug_config(debug.map(Into::into).unwrap_or_default()))
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
pub(crate) base: BaseClientConfig,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn default_debug() -> DebugWasm {
|
||||
ConfigDebug::default().into()
|
||||
impl Config {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id, env!("CARGO_PKG_VERSION").to_string())
|
||||
.with_custom_nyxd(vec![validator_server
|
||||
.parse()
|
||||
.expect("provided url was malformed")])
|
||||
.with_debug_config(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(js_name = defaultDebug)]
|
||||
pub fn default_debug_obj() -> JsValue {
|
||||
let dbg = default_debug();
|
||||
serde_wasm_bindgen::to_value(&dbg)
|
||||
.expect("our 'DebugWasm' struct should have had a correctly defined serde implementation")
|
||||
}
|
||||
|
||||
/// A client configuration in which no cover traffic will be sent,
|
||||
/// in either the main distribution or the secondary traffic stream.
|
||||
#[wasm_bindgen]
|
||||
pub fn no_cover_debug() -> DebugWasm {
|
||||
let mut cfg = ConfigDebug::default();
|
||||
cfg.traffic.disable_main_poisson_packet_distribution = true;
|
||||
cfg.cover_traffic.disable_loop_cover_traffic_stream = true;
|
||||
cfg.into()
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct TrafficWasm {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// sent packet is going to be delayed at any given mix node.
|
||||
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
||||
/// until the packet reaches its destination.
|
||||
pub average_packet_delay_ms: u64,
|
||||
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// it is going to take another 'real traffic stream' message to be sent.
|
||||
/// If no real packets are available and cover traffic is enabled,
|
||||
/// a loop cover message is sent instead in order to preserve the rate.
|
||||
pub message_sending_average_delay_ms: u64,
|
||||
|
||||
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
||||
/// poisson distribution.
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
|
||||
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
|
||||
pub use_extended_packet_size: bool,
|
||||
|
||||
/// Controls whether the sent packets should use outfox as opposed to the default sphinx.
|
||||
pub use_outfox: bool,
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = noCoverDebug)]
|
||||
pub fn no_cover_debug_obj() -> JsValue {
|
||||
let dbg = no_cover_debug();
|
||||
serde_wasm_bindgen::to_value(&dbg)
|
||||
.expect("our 'DebugWasm' struct should have had a correctly defined serde implementation")
|
||||
impl From<TrafficWasm> for ConfigTraffic {
|
||||
fn from(traffic: TrafficWasm) -> Self {
|
||||
let use_extended_packet_size = traffic
|
||||
.use_extended_packet_size
|
||||
.then(|| PacketSize::ExtendedPacket32);
|
||||
|
||||
let packet_type = if traffic.use_outfox {
|
||||
PacketType::Outfox
|
||||
} else {
|
||||
PacketType::Mix
|
||||
};
|
||||
|
||||
ConfigTraffic {
|
||||
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
|
||||
message_sending_average_delay: Duration::from_millis(
|
||||
traffic.message_sending_average_delay_ms,
|
||||
),
|
||||
disable_main_poisson_packet_distribution: traffic
|
||||
.disable_main_poisson_packet_distribution,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
secondary_packet_size: use_extended_packet_size,
|
||||
packet_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigTraffic> for TrafficWasm {
|
||||
fn from(traffic: ConfigTraffic) -> Self {
|
||||
TrafficWasm {
|
||||
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
|
||||
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
|
||||
as u64,
|
||||
disable_main_poisson_packet_distribution: traffic
|
||||
.disable_main_poisson_packet_distribution,
|
||||
use_extended_packet_size: traffic.secondary_packet_size.is_some(),
|
||||
use_outfox: traffic.packet_type == PacketType::Outfox,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct CoverTrafficWasm {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// it is going to take for another loop cover traffic message to be sent.
|
||||
pub loop_cover_traffic_average_delay_ms: u64,
|
||||
|
||||
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
|
||||
/// Only applicable if `secondary_packet_size` is enabled.
|
||||
pub cover_traffic_primary_size_ratio: f64,
|
||||
|
||||
/// Controls whether the dedicated loop cover traffic stream should be enabled.
|
||||
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
|
||||
pub disable_loop_cover_traffic_stream: bool,
|
||||
}
|
||||
|
||||
impl From<CoverTrafficWasm> for ConfigCoverTraffic {
|
||||
fn from(cover_traffic: CoverTrafficWasm) -> Self {
|
||||
ConfigCoverTraffic {
|
||||
loop_cover_traffic_average_delay: Duration::from_millis(
|
||||
cover_traffic.loop_cover_traffic_average_delay_ms,
|
||||
),
|
||||
cover_traffic_primary_size_ratio: cover_traffic.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigCoverTraffic> for CoverTrafficWasm {
|
||||
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
|
||||
CoverTrafficWasm {
|
||||
loop_cover_traffic_average_delay_ms: cover_traffic
|
||||
.loop_cover_traffic_average_delay
|
||||
.as_millis() as u64,
|
||||
cover_traffic_primary_size_ratio: cover_traffic.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct GatewayConnectionWasm {
|
||||
/// How long we're willing to wait for a response to a message sent to the gateway,
|
||||
/// before giving up on it.
|
||||
pub gateway_response_timeout_ms: u64,
|
||||
}
|
||||
|
||||
impl From<GatewayConnectionWasm> for ConfigGatewayConnection {
|
||||
fn from(gateway_connection: GatewayConnectionWasm) -> Self {
|
||||
ConfigGatewayConnection {
|
||||
gateway_response_timeout: Duration::from_millis(
|
||||
gateway_connection.gateway_response_timeout_ms,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigGatewayConnection> for GatewayConnectionWasm {
|
||||
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
|
||||
GatewayConnectionWasm {
|
||||
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
|
||||
as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct AcknowledgementsWasm {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// sent acknowledgement is going to be delayed at any given mix node.
|
||||
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
||||
/// until the packet reaches its destination.
|
||||
pub average_ack_delay_ms: u64,
|
||||
|
||||
/// Value multiplied with the expected round trip time of an acknowledgement packet before
|
||||
/// it is assumed it was lost and retransmission of the data packet happens.
|
||||
/// In an ideal network with 0 latency, this value would have been 1.
|
||||
pub ack_wait_multiplier: f64,
|
||||
|
||||
/// Value added to the expected round trip time of an acknowledgement packet before
|
||||
/// it is assumed it was lost and retransmission of the data packet happens.
|
||||
/// In an ideal network with 0 latency, this value would have been 0.
|
||||
pub ack_wait_addition_ms: u64,
|
||||
}
|
||||
|
||||
impl From<AcknowledgementsWasm> for ConfigAcknowledgements {
|
||||
fn from(acknowledgements: AcknowledgementsWasm) -> Self {
|
||||
ConfigAcknowledgements {
|
||||
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
|
||||
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||
ack_wait_addition: Duration::from_millis(acknowledgements.ack_wait_addition_ms),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigAcknowledgements> for AcknowledgementsWasm {
|
||||
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
|
||||
AcknowledgementsWasm {
|
||||
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
|
||||
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct TopologyWasm {
|
||||
/// The uniform delay every which clients are querying the directory server
|
||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||
pub topology_refresh_rate_ms: u64,
|
||||
|
||||
/// During topology refresh, test packets are sent through every single possible network
|
||||
/// path. This timeout determines waiting period until it is decided that the packet
|
||||
/// did not reach its destination.
|
||||
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 {
|
||||
fn from(topology: TopologyWasm) -> Self {
|
||||
ConfigTopology {
|
||||
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
|
||||
topology_resolution_timeout: Duration::from_millis(
|
||||
topology.topology_resolution_timeout_ms,
|
||||
),
|
||||
disable_refreshing: topology.disable_refreshing,
|
||||
topology_structure: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigTopology> for TopologyWasm {
|
||||
fn from(topology: ConfigTopology) -> Self {
|
||||
TopologyWasm {
|
||||
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
|
||||
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
|
||||
disable_refreshing: topology.disable_refreshing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ReplySurbsWasm {
|
||||
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
||||
/// It can only allow to go below that value if its to request additional reply surbs.
|
||||
pub minimum_reply_surb_storage_threshold: usize,
|
||||
|
||||
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
|
||||
pub maximum_reply_surb_storage_threshold: usize,
|
||||
|
||||
/// Defines the minimum number of reply surbs the client would request.
|
||||
pub minimum_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines the maximum number of reply surbs the client would request.
|
||||
pub maximum_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
|
||||
pub maximum_allowed_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
|
||||
/// for more even though in theory they wouldn't need to.
|
||||
pub maximum_reply_surb_rerequest_waiting_period_ms: u64,
|
||||
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before
|
||||
/// deciding it's never going to get them and would drop all pending messages
|
||||
pub maximum_reply_surb_drop_waiting_period_ms: u64,
|
||||
|
||||
/// Defines maximum amount of time given reply surb is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
pub maximum_reply_surb_age_ms: u64,
|
||||
|
||||
/// Defines maximum amount of time given reply key is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
pub maximum_reply_key_age_ms: u64,
|
||||
}
|
||||
|
||||
impl From<ReplySurbsWasm> for ConfigReplySurbs {
|
||||
fn from(reply_surbs: ReplySurbsWasm) -> Self {
|
||||
ConfigReplySurbs {
|
||||
minimum_reply_surb_storage_threshold: reply_surbs.minimum_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,
|
||||
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size: reply_surbs
|
||||
.maximum_allowed_reply_surb_request_size,
|
||||
maximum_reply_surb_rerequest_waiting_period: Duration::from_millis(
|
||||
reply_surbs.maximum_reply_surb_rerequest_waiting_period_ms,
|
||||
),
|
||||
maximum_reply_surb_drop_waiting_period: Duration::from_millis(
|
||||
reply_surbs.maximum_reply_surb_drop_waiting_period_ms,
|
||||
),
|
||||
maximum_reply_surb_age: Duration::from_millis(reply_surbs.maximum_reply_surb_age_ms),
|
||||
maximum_reply_key_age: Duration::from_millis(reply_surbs.maximum_reply_key_age_ms),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigReplySurbs> for ReplySurbsWasm {
|
||||
fn from(reply_surbs: ConfigReplySurbs) -> Self {
|
||||
ReplySurbsWasm {
|
||||
minimum_reply_surb_storage_threshold: reply_surbs.minimum_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,
|
||||
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size: reply_surbs
|
||||
.maximum_allowed_reply_surb_request_size,
|
||||
maximum_reply_surb_rerequest_waiting_period_ms: reply_surbs
|
||||
.maximum_reply_surb_rerequest_waiting_period
|
||||
.as_millis() as u64,
|
||||
maximum_reply_surb_drop_waiting_period_ms: reply_surbs
|
||||
.maximum_reply_surb_drop_waiting_period
|
||||
.as_millis() as u64,
|
||||
maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u64,
|
||||
maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// just a helper structure to more easily pass through the JS boundary
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct DebugWasm {
|
||||
/// Defines all configuration options related to traffic streams.
|
||||
pub traffic: TrafficWasm,
|
||||
@@ -110,12 +362,6 @@ pub struct DebugWasm {
|
||||
pub reply_surbs: ReplySurbsWasm,
|
||||
}
|
||||
|
||||
impl Default for DebugWasm {
|
||||
fn default() -> Self {
|
||||
ConfigDebug::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DebugWasm> for ConfigDebug {
|
||||
fn from(debug: DebugWasm) -> Self {
|
||||
ConfigDebug {
|
||||
@@ -142,340 +388,7 @@ impl From<ConfigDebug> for DebugWasm {
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TrafficWasm {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// sent packet is going to be delayed at any given mix node.
|
||||
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
||||
/// until the packet reaches its destination.
|
||||
pub average_packet_delay_ms: u32,
|
||||
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// it is going to take another 'real traffic stream' message to be sent.
|
||||
/// If no real packets are available and cover traffic is enabled,
|
||||
/// a loop cover message is sent instead in order to preserve the rate.
|
||||
pub message_sending_average_delay_ms: u32,
|
||||
|
||||
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
||||
/// poisson distribution.
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
|
||||
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
|
||||
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 Default for TrafficWasm {
|
||||
fn default() -> Self {
|
||||
ConfigTraffic::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TrafficWasm> for ConfigTraffic {
|
||||
fn from(traffic: TrafficWasm) -> Self {
|
||||
let use_extended_packet_size = traffic
|
||||
.use_extended_packet_size
|
||||
.then_some(PacketSize::ExtendedPacket32);
|
||||
|
||||
let packet_type = if traffic.use_outfox {
|
||||
PacketType::Outfox
|
||||
} else {
|
||||
PacketType::Mix
|
||||
};
|
||||
|
||||
ConfigTraffic {
|
||||
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms as u64),
|
||||
message_sending_average_delay: Duration::from_millis(
|
||||
traffic.message_sending_average_delay_ms as u64,
|
||||
),
|
||||
disable_main_poisson_packet_distribution: traffic
|
||||
.disable_main_poisson_packet_distribution,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
secondary_packet_size: use_extended_packet_size,
|
||||
packet_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigTraffic> for TrafficWasm {
|
||||
fn from(traffic: ConfigTraffic) -> Self {
|
||||
TrafficWasm {
|
||||
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u32,
|
||||
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
|
||||
as u32,
|
||||
disable_main_poisson_packet_distribution: traffic
|
||||
.disable_main_poisson_packet_distribution,
|
||||
use_extended_packet_size: traffic.secondary_packet_size.is_some(),
|
||||
use_outfox: traffic.packet_type == PacketType::Outfox,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CoverTrafficWasm {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// it is going to take for another loop cover traffic message to be sent.
|
||||
pub loop_cover_traffic_average_delay_ms: u32,
|
||||
|
||||
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
|
||||
/// Only applicable if `secondary_packet_size` is enabled.
|
||||
pub cover_traffic_primary_size_ratio: f64,
|
||||
|
||||
/// Controls whether the dedicated loop cover traffic stream should be enabled.
|
||||
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
|
||||
pub disable_loop_cover_traffic_stream: bool,
|
||||
}
|
||||
|
||||
impl Default for CoverTrafficWasm {
|
||||
fn default() -> Self {
|
||||
ConfigCoverTraffic::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CoverTrafficWasm> for ConfigCoverTraffic {
|
||||
fn from(cover_traffic: CoverTrafficWasm) -> Self {
|
||||
ConfigCoverTraffic {
|
||||
loop_cover_traffic_average_delay: Duration::from_millis(
|
||||
cover_traffic.loop_cover_traffic_average_delay_ms as u64,
|
||||
),
|
||||
cover_traffic_primary_size_ratio: cover_traffic.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigCoverTraffic> for CoverTrafficWasm {
|
||||
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
|
||||
CoverTrafficWasm {
|
||||
loop_cover_traffic_average_delay_ms: cover_traffic
|
||||
.loop_cover_traffic_average_delay
|
||||
.as_millis() as u32,
|
||||
cover_traffic_primary_size_ratio: cover_traffic.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GatewayConnectionWasm {
|
||||
/// How long we're willing to wait for a response to a message sent to the gateway,
|
||||
/// before giving up on it.
|
||||
pub gateway_response_timeout_ms: u32,
|
||||
}
|
||||
|
||||
impl Default for GatewayConnectionWasm {
|
||||
fn default() -> Self {
|
||||
ConfigGatewayConnection::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GatewayConnectionWasm> for ConfigGatewayConnection {
|
||||
fn from(gateway_connection: GatewayConnectionWasm) -> Self {
|
||||
ConfigGatewayConnection {
|
||||
gateway_response_timeout: Duration::from_millis(
|
||||
gateway_connection.gateway_response_timeout_ms as u64,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigGatewayConnection> for GatewayConnectionWasm {
|
||||
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
|
||||
GatewayConnectionWasm {
|
||||
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
|
||||
as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AcknowledgementsWasm {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// sent acknowledgement is going to be delayed at any given mix node.
|
||||
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
||||
/// until the packet reaches its destination.
|
||||
pub average_ack_delay_ms: u32,
|
||||
|
||||
/// Value multiplied with the expected round trip time of an acknowledgement packet before
|
||||
/// it is assumed it was lost and retransmission of the data packet happens.
|
||||
/// In an ideal network with 0 latency, this value would have been 1.
|
||||
pub ack_wait_multiplier: f64,
|
||||
|
||||
/// Value added to the expected round trip time of an acknowledgement packet before
|
||||
/// it is assumed it was lost and retransmission of the data packet happens.
|
||||
/// In an ideal network with 0 latency, this value would have been 0.
|
||||
pub ack_wait_addition_ms: u32,
|
||||
}
|
||||
|
||||
impl Default for AcknowledgementsWasm {
|
||||
fn default() -> Self {
|
||||
ConfigAcknowledgements::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AcknowledgementsWasm> for ConfigAcknowledgements {
|
||||
fn from(acknowledgements: AcknowledgementsWasm) -> Self {
|
||||
ConfigAcknowledgements {
|
||||
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms as u64),
|
||||
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||
ack_wait_addition: Duration::from_millis(acknowledgements.ack_wait_addition_ms as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigAcknowledgements> for AcknowledgementsWasm {
|
||||
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
|
||||
AcknowledgementsWasm {
|
||||
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u32,
|
||||
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TopologyWasm {
|
||||
/// The uniform delay every which clients are querying the directory server
|
||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||
pub topology_refresh_rate_ms: u32,
|
||||
|
||||
/// During topology refresh, test packets are sent through every single possible network
|
||||
/// path. This timeout determines waiting period until it is decided that the packet
|
||||
/// did not reach its destination.
|
||||
pub topology_resolution_timeout_ms: u32,
|
||||
|
||||
/// 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 Default for TopologyWasm {
|
||||
fn default() -> Self {
|
||||
ConfigTopology::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TopologyWasm> for ConfigTopology {
|
||||
fn from(topology: TopologyWasm) -> Self {
|
||||
ConfigTopology {
|
||||
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms as u64),
|
||||
topology_resolution_timeout: Duration::from_millis(
|
||||
topology.topology_resolution_timeout_ms as u64,
|
||||
),
|
||||
disable_refreshing: topology.disable_refreshing,
|
||||
topology_structure: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigTopology> for TopologyWasm {
|
||||
fn from(topology: ConfigTopology) -> Self {
|
||||
TopologyWasm {
|
||||
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u32,
|
||||
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u32,
|
||||
disable_refreshing: topology.disable_refreshing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ReplySurbsWasm {
|
||||
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
||||
/// It can only allow to go below that value if its to request additional reply surbs.
|
||||
pub minimum_reply_surb_storage_threshold: usize,
|
||||
|
||||
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
|
||||
pub maximum_reply_surb_storage_threshold: usize,
|
||||
|
||||
/// Defines the minimum number of reply surbs the client would request.
|
||||
pub minimum_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines the maximum number of reply surbs the client would request.
|
||||
pub maximum_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
|
||||
pub maximum_allowed_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
|
||||
/// for more even though in theory they wouldn't need to.
|
||||
pub maximum_reply_surb_rerequest_waiting_period_ms: u32,
|
||||
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before
|
||||
/// deciding it's never going to get them and would drop all pending messages
|
||||
pub maximum_reply_surb_drop_waiting_period_ms: u32,
|
||||
|
||||
/// Defines maximum amount of time given reply surb is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
pub maximum_reply_surb_age_ms: u32,
|
||||
|
||||
/// Defines maximum amount of time given reply key is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
pub maximum_reply_key_age_ms: u32,
|
||||
}
|
||||
|
||||
impl Default for ReplySurbsWasm {
|
||||
fn default() -> Self {
|
||||
ConfigReplySurbs::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ReplySurbsWasm> for ConfigReplySurbs {
|
||||
fn from(reply_surbs: ReplySurbsWasm) -> Self {
|
||||
ConfigReplySurbs {
|
||||
minimum_reply_surb_storage_threshold: reply_surbs.minimum_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,
|
||||
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size: reply_surbs
|
||||
.maximum_allowed_reply_surb_request_size,
|
||||
maximum_reply_surb_rerequest_waiting_period: Duration::from_millis(
|
||||
reply_surbs.maximum_reply_surb_rerequest_waiting_period_ms as u64,
|
||||
),
|
||||
maximum_reply_surb_drop_waiting_period: Duration::from_millis(
|
||||
reply_surbs.maximum_reply_surb_drop_waiting_period_ms as u64,
|
||||
),
|
||||
maximum_reply_surb_age: Duration::from_millis(
|
||||
reply_surbs.maximum_reply_surb_age_ms as u64,
|
||||
),
|
||||
maximum_reply_key_age: Duration::from_millis(
|
||||
reply_surbs.maximum_reply_key_age_ms as u64,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigReplySurbs> for ReplySurbsWasm {
|
||||
fn from(reply_surbs: ConfigReplySurbs) -> Self {
|
||||
ReplySurbsWasm {
|
||||
minimum_reply_surb_storage_threshold: reply_surbs.minimum_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,
|
||||
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size: reply_surbs
|
||||
.maximum_allowed_reply_surb_request_size,
|
||||
maximum_reply_surb_rerequest_waiting_period_ms: reply_surbs
|
||||
.maximum_reply_surb_rerequest_waiting_period
|
||||
.as_millis() as u32,
|
||||
maximum_reply_surb_drop_waiting_period_ms: reply_surbs
|
||||
.maximum_reply_surb_drop_waiting_period
|
||||
.as_millis() as u32,
|
||||
maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u32,
|
||||
maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u32,
|
||||
}
|
||||
}
|
||||
#[wasm_bindgen]
|
||||
pub fn default_debug() -> DebugWasm {
|
||||
ConfigDebug::default().into()
|
||||
}
|
||||
@@ -1,28 +1,20 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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 nym_client_core::client::base_client::{ClientInput, ClientState};
|
||||
use nym_client_core::client::inbound_messages::InputMessage;
|
||||
use nym_topology::{MixLayer, NymTopology};
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_client_core::client::base_client::{ClientInput, ClientState};
|
||||
use wasm_client_core::client::inbound_messages::InputMessage;
|
||||
use wasm_client_core::error::WasmCoreError;
|
||||
use wasm_client_core::topology::SerializableNymTopology;
|
||||
use wasm_client_core::{MixLayer, NymTopology};
|
||||
use wasm_utils::error::simple_js_error;
|
||||
use wasm_utils::{check_promise_result, console_log};
|
||||
use wasm_utils::{console_log, simple_js_error};
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
use nym_node_tester_wasm::types::{NodeTestMessage, WasmTestMessageExt};
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20;
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
#[wasm_bindgen]
|
||||
pub struct NymClientTestRequest {
|
||||
// serialized NodeTestMessage
|
||||
@@ -33,10 +25,9 @@ pub struct NymClientTestRequest {
|
||||
pub(crate) testable_topology: NymTopology,
|
||||
}
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
#[wasm_bindgen]
|
||||
impl NymClientTestRequest {
|
||||
pub fn injectable_topology(&self) -> SerializableNymTopology {
|
||||
pub fn injectable_topology(&self) -> WasmNymTopology {
|
||||
self.testable_topology.clone().into()
|
||||
}
|
||||
}
|
||||
@@ -78,17 +69,14 @@ impl InputSender for Arc<ClientInput> {
|
||||
|
||||
pub(crate) trait WasmTopologyExt {
|
||||
/// Changes the current network topology to the provided value.
|
||||
fn change_hardcoded_topology(&self, topology: SerializableNymTopology) -> Promise;
|
||||
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;
|
||||
}
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
pub(crate) trait WasmTopologyTestExt {
|
||||
/// 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,
|
||||
@@ -99,11 +87,10 @@ pub(crate) trait WasmTopologyTestExt {
|
||||
}
|
||||
|
||||
impl WasmTopologyExt for Arc<ClientState> {
|
||||
fn change_hardcoded_topology(&self, topology: SerializableNymTopology) -> Promise {
|
||||
let nym_topology: NymTopology = check_promise_result!(topology.try_into());
|
||||
|
||||
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)
|
||||
@@ -116,11 +103,8 @@ impl WasmTopologyExt for Arc<ClientState> {
|
||||
let this = Arc::clone(self);
|
||||
future_to_promise(async move {
|
||||
match this.topology_accessor.current_topology().await {
|
||||
Some(topology) => Ok(serde_wasm_bindgen::to_value(&SerializableNymTopology::from(
|
||||
topology,
|
||||
))
|
||||
.expect("SerializableNymTopology failed serialization")),
|
||||
None => Err(WasmCoreError::UnavailableNetworkTopology.into()),
|
||||
Some(topology) => Ok(JsValue::from(WasmNymTopology::from(topology))),
|
||||
None => Err(WasmClientError::UnavailableNetworkTopology.into()),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -130,19 +114,16 @@ impl WasmTopologyExt for Arc<ClientState> {
|
||||
let this = Arc::clone(self);
|
||||
future_to_promise(async move {
|
||||
let Some(current_topology) = this.topology_accessor.current_topology().await else {
|
||||
return Err(WasmCoreError::UnavailableNetworkTopology.into());
|
||||
return Err(WasmClientError::UnavailableNetworkTopology.into());
|
||||
};
|
||||
|
||||
match current_topology.find_mix_by_identity(&mixnode_identity) {
|
||||
None => Err(WasmCoreError::NonExistentMixnode { mixnode_identity }.into()),
|
||||
None => Err(WasmClientError::NonExistentMixnode { mixnode_identity }.into()),
|
||||
Some(node) => Ok(JsValue::from(MixLayer::from(node.layer))),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
impl WasmTopologyTestExt for Arc<ClientState> {
|
||||
fn mix_test_request(
|
||||
&self,
|
||||
test_id: u32,
|
||||
@@ -154,16 +135,16 @@ impl WasmTopologyTestExt for Arc<ClientState> {
|
||||
let this = Arc::clone(self);
|
||||
future_to_promise(async move {
|
||||
let Some(current_topology) = this.topology_accessor.current_topology().await else {
|
||||
return Err(WasmCoreError::UnavailableNetworkTopology.into());
|
||||
return Err(WasmClientError::UnavailableNetworkTopology.into());
|
||||
};
|
||||
|
||||
let Some(mix) = current_topology.find_mix_by_identity(&mixnode_identity) else {
|
||||
return Err(WasmCoreError::NonExistentMixnode { mixnode_identity }.into());
|
||||
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(crate::error::WasmClientError::from)?;
|
||||
.map_err(WasmClientError::from)?;
|
||||
|
||||
let mut updated = current_topology.clone();
|
||||
updated.set_mixes_in_layer(mix.layer.into(), vec![mix.to_owned()]);
|
||||
@@ -1,45 +1,41 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::{ClientConfig, ClientConfigOpts};
|
||||
use self::config::Config;
|
||||
use crate::client::helpers::{InputSender, NymClientTestRequest, WasmTopologyExt};
|
||||
use crate::client::response_pusher::ResponsePusher;
|
||||
use crate::constants::NODE_TESTER_CLIENT_ID;
|
||||
use crate::error::WasmClientError;
|
||||
use crate::helpers::{InputSender, WasmTopologyExt};
|
||||
use crate::response_pusher::ResponsePusher;
|
||||
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 serde::{Deserialize, Serialize};
|
||||
use nym_client_core::client::base_client::{
|
||||
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use nym_client_core::client::inbound_messages::InputMessage;
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use nym_task::TaskManager;
|
||||
use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
|
||||
use nym_topology::NymTopology;
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
use nym_validator_client::QueryReqwestRpcNyxdClient;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use std::sync::Arc;
|
||||
use tsify::Tsify;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_client_core::client::{
|
||||
base_client::{BaseClientBuilder, ClientInput, ClientOutput, ClientState},
|
||||
inbound_messages::InputMessage,
|
||||
};
|
||||
use wasm_client_core::config::r#override::DebugWasmOverride;
|
||||
use wasm_client_core::helpers::{
|
||||
parse_recipient, parse_sender_tag, setup_from_topology, setup_gateway_from_api,
|
||||
};
|
||||
use wasm_client_core::init::GatewaySetup;
|
||||
use wasm_client_core::nym_task::connections::TransmissionLane;
|
||||
use wasm_client_core::nym_task::TaskManager;
|
||||
use wasm_client_core::storage::core_client_traits::FullWasmClientStorage;
|
||||
use wasm_client_core::storage::ClientStorage;
|
||||
use wasm_client_core::topology::{SerializableNymTopology, SerializableTopologyExt};
|
||||
use wasm_client_core::{
|
||||
HardcodedTopologyProvider, IdentityKey, NymTopology, PacketType, QueryReqwestRpcNyxdClient,
|
||||
TopologyProvider,
|
||||
};
|
||||
use wasm_utils::error::PromisableResult;
|
||||
use wasm_utils::{check_promise_result, console_log};
|
||||
use wasm_utils::{check_promise_result, console_log, PromisableResult};
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
use crate::helpers::{NymClientTestRequest, WasmTopologyTestExt};
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
pub(crate) const NODE_TESTER_CLIENT_ID: &str = "_nym-node-tester-client";
|
||||
pub mod config;
|
||||
mod helpers;
|
||||
mod response_pusher;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct NymClient {
|
||||
@@ -58,11 +54,9 @@ pub struct NymClient {
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
// TODO: we don't really need a builder anymore,
|
||||
// but we might as well leave it for backwards compatibility
|
||||
#[wasm_bindgen]
|
||||
pub struct NymClientBuilder {
|
||||
config: ClientConfig,
|
||||
config: Config,
|
||||
custom_topology: Option<NymTopology>,
|
||||
preferred_gateway: Option<IdentityKey>,
|
||||
|
||||
@@ -74,7 +68,7 @@ pub struct NymClientBuilder {
|
||||
impl NymClientBuilder {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(
|
||||
config: ClientConfig,
|
||||
config: Config,
|
||||
on_message: js_sys::Function,
|
||||
preferred_gateway: Option<IdentityKey>,
|
||||
storage_passphrase: Option<String>,
|
||||
@@ -84,7 +78,6 @@ impl NymClientBuilder {
|
||||
custom_topology: None,
|
||||
storage_passphrase,
|
||||
on_message,
|
||||
// on_mix_fetch_message: Some(on_mix_fetch_message),
|
||||
preferred_gateway,
|
||||
}
|
||||
}
|
||||
@@ -93,27 +86,26 @@ impl NymClientBuilder {
|
||||
// no poisson delay
|
||||
// hardcoded topology
|
||||
// NOTE: you most likely want to use `[NymNodeTester]` instead.
|
||||
#[cfg(feature = "node-tester")]
|
||||
pub fn new_tester(
|
||||
topology: SerializableNymTopology,
|
||||
topology: WasmNymTopology,
|
||||
on_message: js_sys::Function,
|
||||
gateway: Option<IdentityKey>,
|
||||
) -> Result<NymClientBuilder, WasmClientError> {
|
||||
) -> Self {
|
||||
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 = ClientConfig::new_tester_config(NODE_TESTER_CLIENT_ID);
|
||||
let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID);
|
||||
|
||||
Ok(NymClientBuilder {
|
||||
NymClientBuilder {
|
||||
config: full_config,
|
||||
custom_topology: Some(topology.try_into()?),
|
||||
custom_topology: Some(topology.into()),
|
||||
on_message,
|
||||
storage_passphrase: None,
|
||||
preferred_gateway: gateway,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn start_reconstructed_pusher(client_output: ClientOutput, on_message: js_sys::Function) {
|
||||
@@ -128,11 +120,12 @@ impl NymClientBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
fn initialise_storage(
|
||||
config: &ClientConfig,
|
||||
base_storage: ClientStorage,
|
||||
) -> FullWasmClientStorage {
|
||||
FullWasmClientStorage::new(&config.base, base_storage)
|
||||
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> {
|
||||
@@ -148,7 +141,7 @@ impl NymClientBuilder {
|
||||
let user_chosen = self.preferred_gateway.clone();
|
||||
|
||||
// if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way
|
||||
let init_res = if let Some(topology) = &self.custom_topology {
|
||||
if let Some(topology) = &self.custom_topology {
|
||||
setup_from_topology(user_chosen, topology, &client_store).await?
|
||||
} else {
|
||||
setup_gateway_from_api(&client_store, user_chosen, &nym_api_endpoints).await?
|
||||
@@ -158,20 +151,15 @@ impl NymClientBuilder {
|
||||
let storage = Self::initialise_storage(&self.config, client_store);
|
||||
let maybe_topology_provider = self.topology_provider();
|
||||
|
||||
let mut base_builder = BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new(
|
||||
&self.config.base,
|
||||
storage,
|
||||
None,
|
||||
);
|
||||
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> =
|
||||
BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new(
|
||||
&self.config.base,
|
||||
storage,
|
||||
None,
|
||||
);
|
||||
if let Some(topology_provider) = maybe_topology_provider {
|
||||
base_builder = base_builder.with_topology_provider(topology_provider);
|
||||
}
|
||||
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
|
||||
base_builder = base_builder.with_gateway_setup(GatewaySetup::ReuseConnection {
|
||||
authenticated_ephemeral_client,
|
||||
details: init_res.details,
|
||||
});
|
||||
}
|
||||
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
let self_address = started_client.address.to_string();
|
||||
@@ -196,113 +184,38 @@ impl NymClientBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClientOptsSimple {
|
||||
// ideally we'd have used the `IdentityKey` type alias, but that'd be extremely annoying to get working in TS
|
||||
#[tsify(optional)]
|
||||
pub(crate) preferred_gateway: Option<String>,
|
||||
|
||||
#[tsify(optional)]
|
||||
pub(crate) storage_passphrase: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Tsify, Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClientOpts {
|
||||
#[serde(flatten)]
|
||||
pub(crate) base: Option<ClientOptsSimple>,
|
||||
|
||||
#[tsify(optional)]
|
||||
pub(crate) client_id: Option<String>,
|
||||
|
||||
#[tsify(optional)]
|
||||
pub(crate) nym_api_url: Option<String>,
|
||||
|
||||
// currently not used, but will be required once we have coconut
|
||||
#[tsify(optional)]
|
||||
pub(crate) nyxd_url: Option<String>,
|
||||
|
||||
#[tsify(optional)]
|
||||
pub(crate) client_override: Option<DebugWasmOverride>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ClientOpts> for ClientConfigOpts {
|
||||
fn from(value: &'a ClientOpts) -> Self {
|
||||
ClientConfigOpts {
|
||||
id: value.client_id.as_ref().map(|v| v.to_owned()),
|
||||
nym_api: value.nym_api_url.as_ref().map(|v| v.to_owned()),
|
||||
nyxd: value.nyxd_url.as_ref().map(|v| v.to_owned()),
|
||||
debug: value.client_override.as_ref().map(|&v| v.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl NymClient {
|
||||
async fn _new(
|
||||
config: ClientConfig,
|
||||
config: Config,
|
||||
on_message: js_sys::Function,
|
||||
opts: Option<ClientOptsSimple>,
|
||||
preferred_gateway: Option<IdentityKey>,
|
||||
storage_passphrase: Option<String>,
|
||||
) -> Result<NymClient, WasmClientError> {
|
||||
if let Some(opts) = opts {
|
||||
let preferred_gateway = opts.preferred_gateway;
|
||||
let storage_passphrase = opts.storage_passphrase;
|
||||
NymClientBuilder::new(config, on_message, preferred_gateway, storage_passphrase)
|
||||
} else {
|
||||
NymClientBuilder::new(config, on_message, None, None)
|
||||
}
|
||||
.start_client_async()
|
||||
.await
|
||||
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(on_message: js_sys::Function, opts: Option<ClientOpts>) -> Promise {
|
||||
let opts = opts.unwrap_or_default();
|
||||
let mut config = check_promise_result!(ClientConfig::new((&opts).into()));
|
||||
|
||||
if let Some(dbg) = opts.client_override {
|
||||
config.override_debug(dbg);
|
||||
}
|
||||
|
||||
future_to_promise(async move {
|
||||
Self::_new(config, on_message, opts.base)
|
||||
.await
|
||||
.into_promise_result()
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "newWithConfig")]
|
||||
pub fn new_with_config(
|
||||
config: ClientConfig,
|
||||
pub fn new(
|
||||
config: Config,
|
||||
on_message: js_sys::Function,
|
||||
opts: ClientOptsSimple,
|
||||
preferred_gateway: Option<IdentityKey>,
|
||||
storage_passphrase: Option<String>,
|
||||
) -> Promise {
|
||||
future_to_promise(async move {
|
||||
Self::_new(config, on_message, Some(opts))
|
||||
Self::_new(config, on_message, preferred_gateway, storage_passphrase)
|
||||
.await
|
||||
.into_promise_result()
|
||||
})
|
||||
}
|
||||
|
||||
// no cover traffic
|
||||
// no poisson delay
|
||||
// hardcoded topology
|
||||
// NOTE: you most likely want to use `[NymNodeTester]` instead.
|
||||
#[cfg(feature = "node-tester")]
|
||||
#[wasm_bindgen(js_name = "newTester")]
|
||||
pub fn new_tester() -> Promise {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn self_address(&self) -> String {
|
||||
self.self_address.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "node-tester")]
|
||||
pub fn try_construct_test_packet_request(
|
||||
&self,
|
||||
mixnode_identity: String,
|
||||
@@ -315,7 +228,7 @@ impl NymClient {
|
||||
.mix_test_request(test_id, mixnode_identity, num_test_packets)
|
||||
}
|
||||
|
||||
pub fn change_hardcoded_topology(&self, topology: SerializableNymTopology) -> Promise {
|
||||
pub fn change_hardcoded_topology(&self, topology: WasmNymTopology) -> Promise {
|
||||
self.client_state.change_hardcoded_topology(topology)
|
||||
}
|
||||
|
||||
@@ -326,7 +239,6 @@ impl NymClient {
|
||||
/// 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.
|
||||
#[cfg(feature = "node-tester")]
|
||||
pub fn try_send_test_packets(&mut self, request: NymClientTestRequest) -> Promise {
|
||||
// TOOD: use the premade packets instead
|
||||
console_log!(
|
||||
+5
-9
@@ -4,12 +4,12 @@
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use js_sys::Uint8Array;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use wasm_client_core::client::base_client::ClientOutput;
|
||||
use wasm_client_core::client::received_buffer::{
|
||||
use nym_client_core::client::base_client::ClientOutput;
|
||||
use nym_client_core::client::received_buffer::{
|
||||
ReceivedBufferMessage, ReconstructedMessagesReceiver,
|
||||
};
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use wasm_utils::console_error;
|
||||
|
||||
pub(crate) struct ResponsePusher {
|
||||
@@ -18,11 +18,7 @@ pub(crate) struct ResponsePusher {
|
||||
}
|
||||
|
||||
impl ResponsePusher {
|
||||
pub(crate) fn new(
|
||||
client_output: ClientOutput,
|
||||
on_message: js_sys::Function,
|
||||
// on_mix_fetch_message: js_sys::Function,
|
||||
) -> Self {
|
||||
pub(crate) fn new(client_output: ClientOutput, on_message: js_sys::Function) -> Self {
|
||||
// register our output
|
||||
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub(crate) const NODE_TESTER_ID: &str = "_nym-node-tester";
|
||||
pub(crate) const NODE_TESTER_CLIENT_ID: &str = "_nym-node-tester-client";
|
||||
@@ -1,22 +1,30 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::storage::wasm_client_traits::WasmClientStorageError;
|
||||
use crate::storage::error::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_utils::wasm_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 WasmCoreError {
|
||||
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]
|
||||
@@ -50,6 +58,12 @@ pub enum WasmCoreError {
|
||||
source: NymTopologyError,
|
||||
},
|
||||
|
||||
#[error("failed to test the node: {source}")]
|
||||
NodeTestingFailure {
|
||||
#[from]
|
||||
source: NetworkTestingError,
|
||||
},
|
||||
|
||||
#[error("{raw} is not a valid url: {source}")]
|
||||
MalformedUrl {
|
||||
raw: String,
|
||||
@@ -78,15 +92,9 @@ pub enum WasmCoreError {
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
BaseStorageError {
|
||||
StorageError {
|
||||
#[from]
|
||||
source: wasm_storage::error::StorageError,
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
ClientStorageError {
|
||||
#[from]
|
||||
source: WasmClientStorageError,
|
||||
source: ClientStorageError,
|
||||
},
|
||||
|
||||
#[error("this client has already registered with a gateway: {gateway_config:?}")]
|
||||
@@ -95,4 +103,20 @@ pub enum WasmCoreError {
|
||||
},
|
||||
}
|
||||
|
||||
wasm_error!(WasmCoreError);
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn get_gateway(api_server: String, preferred: Option<String>) -> GatewayEndpointConfig {
|
||||
let validator_client =
|
||||
nym_validator_client::client::NymApiClient::new(api_server.parse().unwrap());
|
||||
|
||||
let gateways = match validator_client.get_cached_gateways().await {
|
||||
Err(err) => panic!("failed to obtain list of all gateways - {err}"),
|
||||
Ok(gateways) => gateways,
|
||||
};
|
||||
|
||||
if let Some(preferred) = preferred {
|
||||
if let Some(details) = gateways
|
||||
.iter()
|
||||
.find(|g| g.gateway.identity_key == preferred)
|
||||
{
|
||||
return GatewayEndpointConfig {
|
||||
gateway_id: details.gateway.identity_key.clone(),
|
||||
gateway_owner: details.owner.to_string(),
|
||||
gateway_listener: format!(
|
||||
"ws://{}:{}",
|
||||
details.gateway.host, details.gateway.clients_port
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let details = gateways
|
||||
.first()
|
||||
.expect("current topology holds no gateways");
|
||||
|
||||
GatewayEndpointConfig {
|
||||
gateway_id: details.gateway.identity_key.clone(),
|
||||
gateway_owner: details.owner.to_string(),
|
||||
gateway_listener: format!(
|
||||
"ws://{}:{}",
|
||||
details.gateway.host, details.gateway.clients_port
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::WasmCoreError;
|
||||
use crate::storage::wasm_client_traits::WasmClientStorage;
|
||||
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;
|
||||
@@ -11,51 +11,51 @@ use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationResult};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_topology::{gateway, NymTopology, SerializableNymTopology};
|
||||
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::error::PromisableResult;
|
||||
|
||||
pub use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
||||
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 fn setup_reply_surb_storage_backend(config: config::ReplySurbs) -> browser_backend::Backend {
|
||||
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 fn parse_recipient(recipient: &str) -> Result<Recipient, WasmCoreError> {
|
||||
pub(crate) fn parse_recipient(recipient: &str) -> Result<Recipient, WasmClientError> {
|
||||
Recipient::try_from_base58_string(recipient).map_err(|source| {
|
||||
WasmCoreError::MalformedRecipient {
|
||||
WasmClientError::MalformedRecipient {
|
||||
raw: recipient.to_string(),
|
||||
source,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_sender_tag(tag: &str) -> Result<AnonymousSenderTag, WasmCoreError> {
|
||||
pub(crate) fn parse_sender_tag(tag: &str) -> Result<AnonymousSenderTag, WasmClientError> {
|
||||
AnonymousSenderTag::try_from_base58_string(tag).map_err(|source| {
|
||||
WasmCoreError::MalformedSenderTag {
|
||||
WasmClientError::MalformedSenderTag {
|
||||
raw: tag.to_string(),
|
||||
source,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn current_network_topology_async(
|
||||
pub(crate) async fn current_network_topology_async(
|
||||
nym_api_url: String,
|
||||
) -> Result<SerializableNymTopology, WasmCoreError> {
|
||||
) -> Result<WasmNymTopology, WasmClientError> {
|
||||
let url: Url = match nym_api_url.parse() {
|
||||
Ok(url) => url,
|
||||
Err(source) => {
|
||||
return Err(WasmCoreError::MalformedUrl {
|
||||
return Err(WasmClientError::MalformedUrl {
|
||||
raw: nym_api_url,
|
||||
source,
|
||||
})
|
||||
@@ -69,13 +69,11 @@ pub async fn current_network_topology_async(
|
||||
Ok(NymTopology::from_detailed(mixnodes, gateways).into())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "currentNetworkTopology")]
|
||||
#[wasm_bindgen]
|
||||
pub fn current_network_topology(nym_api_url: String) -> Promise {
|
||||
// blame js for that serde conversion
|
||||
future_to_promise(async move {
|
||||
current_network_topology_async(nym_api_url)
|
||||
.await
|
||||
.map(|topology| serde_wasm_bindgen::to_value(&topology).unwrap())
|
||||
.into_promise_result()
|
||||
})
|
||||
}
|
||||
@@ -84,7 +82,7 @@ async fn setup_gateway(
|
||||
client_store: &ClientStorage,
|
||||
chosen_gateway: Option<IdentityKey>,
|
||||
gateways: &[gateway::Node],
|
||||
) -> Result<InitialisationResult, WasmCoreError> {
|
||||
) -> Result<InitialisationResult, WasmClientError> {
|
||||
let setup = if client_store.has_full_gateway_info().await? {
|
||||
GatewaySetup::MustLoad
|
||||
} else {
|
||||
@@ -96,21 +94,21 @@ async fn setup_gateway(
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn setup_gateway_from_api(
|
||||
pub(crate) async fn setup_gateway_from_api(
|
||||
client_store: &ClientStorage,
|
||||
chosen_gateway: Option<IdentityKey>,
|
||||
nym_apis: &[Url],
|
||||
) -> Result<InitialisationResult, WasmCoreError> {
|
||||
) -> Result<InitialisationResult, WasmClientError> {
|
||||
let mut rng = thread_rng();
|
||||
let gateways = current_gateways(&mut rng, nym_apis).await?;
|
||||
setup_gateway(client_store, chosen_gateway, &gateways).await
|
||||
}
|
||||
|
||||
pub async fn setup_from_topology(
|
||||
pub(crate) async fn setup_from_topology(
|
||||
explicit_gateway: Option<IdentityKey>,
|
||||
topology: &NymTopology,
|
||||
client_store: &ClientStorage,
|
||||
) -> Result<InitialisationResult, WasmCoreError> {
|
||||
) -> Result<InitialisationResult, WasmClientError> {
|
||||
let gateways = topology.gateways();
|
||||
setup_gateway(client_store, explicit_gateway, gateways).await
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// After reading https://github.com/rust-lang/rust-clippy/issues/11382
|
||||
// I suspect we *maybe* have hit a false positive, but I'm not sure.
|
||||
#![allow(clippy::arc_with_non_send_sync)]
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod client;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod encoded_payload_helper;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod error;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod gateway_selector;
|
||||
#[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;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod helpers;
|
||||
|
||||
mod constants;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::storage::error::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 error;
|
||||
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)
|
||||
}
|
||||
}
|
||||
+4
-18
@@ -1,10 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::BaseClientConfig;
|
||||
use crate::error::WasmCoreError;
|
||||
use crate::helpers::setup_reply_surb_storage_backend;
|
||||
use crate::storage::wasm_client_traits::WasmClientStorage;
|
||||
use crate::storage::error::ClientStorageError;
|
||||
use crate::storage::ClientStorage;
|
||||
use async_trait::async_trait;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
||||
@@ -25,17 +22,6 @@ pub struct FullWasmClientStorage {
|
||||
pub(crate) credential_storage: EphemeralCredentialStorage,
|
||||
}
|
||||
|
||||
impl FullWasmClientStorage {
|
||||
// TODO: I dont like that base_config type, it should be something wasm-specific.
|
||||
pub fn new(base_config: &BaseClientConfig, base_storage: ClientStorage) -> Self {
|
||||
FullWasmClientStorage {
|
||||
keys_and_gateway_store: base_storage,
|
||||
reply_storage: setup_reply_surb_storage_backend(base_config.debug.reply_surbs),
|
||||
credential_storage: EphemeralCredentialStorage::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MixnetClientStorage for FullWasmClientStorage {
|
||||
type KeyStore = ClientStorage;
|
||||
type ReplyStore = browser_backend::Backend;
|
||||
@@ -66,7 +52,7 @@ impl MixnetClientStorage for FullWasmClientStorage {
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl KeyStore for ClientStorage {
|
||||
type StorageError = WasmCoreError;
|
||||
type StorageError = ClientStorageError;
|
||||
|
||||
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
|
||||
console_log!("attempting to load cryptographic keys...");
|
||||
@@ -100,7 +86,7 @@ impl KeyStore for ClientStorage {
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl GatewayDetailsStore for ClientStorage {
|
||||
type StorageError = WasmCoreError;
|
||||
type StorageError = ClientStorageError;
|
||||
|
||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
||||
self.must_read_gateway_details().await
|
||||
@@ -110,6 +96,6 @@ impl GatewayDetailsStore for ClientStorage {
|
||||
&self,
|
||||
details: &PersistedGatewayDetails,
|
||||
) -> Result<(), Self::StorageError> {
|
||||
<Self as WasmClientStorage>::store_gateway_details(self, details).await
|
||||
self.store_gateway_details(details).await
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{NodeTestResult, WasmTestMessageExt};
|
||||
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_node_tester_utils::FragmentIdentifier;
|
||||
use nym_sphinx::chunking::fragment::FragmentIdentifier;
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::MutexGuard as AsyncMutexGuard;
|
||||
@@ -4,18 +4,19 @@
|
||||
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
|
||||
#![allow(clippy::drop_non_drop)]
|
||||
|
||||
use crate::error::NodeTesterError;
|
||||
use crate::error::WasmClientError;
|
||||
use crate::tester::LockedGatewayClient;
|
||||
use crate::types::WasmTestMessageExt;
|
||||
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_warn;
|
||||
use wasm_utils::{console_log, console_warn};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct ReceivedReceiverWrapper(Arc<AsyncMutex<ReceivedReceiver<WasmTestMessageExt>>>);
|
||||
@@ -45,6 +46,55 @@ impl ReceivedReceiverWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
#[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>,
|
||||
}
|
||||
@@ -77,7 +127,7 @@ impl GatewayReconnection for LockedGatewayClient {
|
||||
guard
|
||||
.disconnect()
|
||||
.await
|
||||
.map_err(|err| JsValue::from(NodeTesterError::from(err)))?;
|
||||
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
}
|
||||
@@ -90,7 +140,7 @@ impl GatewayReconnection for LockedGatewayClient {
|
||||
guard
|
||||
.try_reconnect()
|
||||
.await
|
||||
.map_err(|err| JsValue::from(NodeTesterError::from(err)))?;
|
||||
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
}
|
||||
@@ -1,46 +1,52 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ephemeral_receiver::EphemeralTestReceiver;
|
||||
use crate::error::NodeTesterError;
|
||||
use crate::helpers::{GatewayReconnection, ReceivedReceiverWrapper, TestMarker};
|
||||
use crate::types::{NodeTestResult, WasmTestMessageExt};
|
||||
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::BandwidthController;
|
||||
use nym_client_core::client::key_manager::ManagedKeys;
|
||||
use nym_client_core::init::{InitialisationDetails, InitialisationResult};
|
||||
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, PacketSize, PreparedFragment};
|
||||
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 nym_validator_client::QueryReqwestRpcNyxdClient;
|
||||
use rand::rngs::OsRng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
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 tsify::Tsify;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_client_core::helpers::{
|
||||
current_network_topology_async, setup_from_topology, EphemeralCredentialStorage,
|
||||
};
|
||||
use wasm_client_core::storage::ClientStorage;
|
||||
use wasm_client_core::topology::SerializableNymTopology;
|
||||
use wasm_client_core::{
|
||||
nym_task, BandwidthController, GatewayClient, IdentityKey, InitialisationDetails,
|
||||
InitialisationResult, ManagedKeys, NodeIdentity, NymTopology, QueryReqwestRpcNyxdClient,
|
||||
Recipient,
|
||||
};
|
||||
use wasm_utils::check_promise_result;
|
||||
use wasm_utils::error::PromisableResult;
|
||||
use wasm_utils::{check_promise_result, console_log, PromisableResult};
|
||||
|
||||
pub const NODE_TESTER_ID: &str = "_nym-node-tester";
|
||||
mod ephemeral_receiver;
|
||||
pub(crate) mod helpers;
|
||||
|
||||
pub type NodeTestMessage = TestMessage<WasmTestMessageExt>;
|
||||
type LockedGatewayClient =
|
||||
Arc<AsyncMutex<GatewayClient<QueryReqwestRpcNyxdClient, EphemeralStorage>>>;
|
||||
|
||||
pub(crate) const DEFAULT_TEST_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20;
|
||||
|
||||
pub(crate) type LockedGatewayClient =
|
||||
Arc<AsyncMutex<GatewayClient<QueryReqwestRpcNyxdClient, EphemeralCredentialStorage>>>;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct NymNodeTester {
|
||||
test_in_progress: Arc<AtomicBool>,
|
||||
@@ -72,8 +78,7 @@ pub struct NymNodeTesterBuilder {
|
||||
base_topology: NymTopology,
|
||||
|
||||
// unimplemented
|
||||
bandwidth_controller:
|
||||
Option<BandwidthController<QueryReqwestRpcNyxdClient, EphemeralCredentialStorage>>,
|
||||
bandwidth_controller: Option<BandwidthController<QueryReqwestRpcNyxdClient, EphemeralStorage>>,
|
||||
}
|
||||
|
||||
fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
|
||||
@@ -84,69 +89,55 @@ fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NymNodeTesterOpts {
|
||||
#[tsify(optional)]
|
||||
id: Option<String>,
|
||||
|
||||
#[tsify(optional)]
|
||||
nym_api: Option<String>,
|
||||
|
||||
#[tsify(optional)]
|
||||
topology: Option<SerializableNymTopology>,
|
||||
|
||||
#[tsify(optional)]
|
||||
gateway: Option<String>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl NymNodeTesterBuilder {
|
||||
#[wasm_bindgen(constructor)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(args: NymNodeTesterOpts) -> Promise {
|
||||
future_to_promise(async move { Self::new_async(args).await.into_promise_result() })
|
||||
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_async(args: NymNodeTesterOpts) -> Result<NymNodeTesterBuilder, NodeTesterError> {
|
||||
if args.nym_api.is_some() && args.topology.is_some() {
|
||||
return Err(NodeTesterError::DuplicateTopologySource);
|
||||
}
|
||||
if args.nym_api.is_none() && args.topology.is_none() {
|
||||
return Err(NodeTesterError::NoTopologySource);
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
let topology = if let Some(topology) = args.topology {
|
||||
topology
|
||||
} else {
|
||||
// the unwrap here is fine as we just ensured one of the branches would be true
|
||||
current_network_topology_async(args.nym_api.unwrap()).await?
|
||||
};
|
||||
|
||||
Ok(NymNodeTesterBuilder {
|
||||
gateway: args.gateway,
|
||||
id: args.id,
|
||||
base_topology: topology.try_into()?,
|
||||
bandwidth_controller: None,
|
||||
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<InitialisationResult, NodeTesterError> {
|
||||
) -> Result<InitialisationResult, WasmClientError> {
|
||||
if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await {
|
||||
Ok(loaded.into())
|
||||
} else {
|
||||
Ok(
|
||||
setup_from_topology(self.gateway.clone(), &self.base_topology, client_store)
|
||||
.await?,
|
||||
)
|
||||
setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn _setup_client(mut self) -> Result<NymNodeTester, NodeTesterError> {
|
||||
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
|
||||
let task_manager = TaskManager::default();
|
||||
|
||||
let storage_id = if let Some(client_id) = &self.id {
|
||||
@@ -236,7 +227,7 @@ async fn test_mixnode(
|
||||
processed_receiver: ReceivedReceiverWrapper,
|
||||
_test_marker: TestMarker,
|
||||
timeout: Duration,
|
||||
) -> Result<NodeTestResult, NodeTesterError> {
|
||||
) -> Result<NodeTestResult, WasmClientError> {
|
||||
let num_test_packets = test_packets.len() as u32;
|
||||
|
||||
let expected_ack_ids = test_packets
|
||||
@@ -267,17 +258,38 @@ async fn test_mixnode(
|
||||
impl NymNodeTester {
|
||||
#[wasm_bindgen(constructor)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(args: NymNodeTesterOpts) -> Promise {
|
||||
future_to_promise(async move { Self::new_async(args).await.into_promise_result() })
|
||||
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_async(args: NymNodeTesterOpts) -> Result<Self, NodeTesterError> {
|
||||
NymNodeTesterBuilder::new_async(args)
|
||||
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()
|
||||
}
|
||||
@@ -291,7 +303,7 @@ impl NymNodeTester {
|
||||
mixnode_identity: String,
|
||||
test_nonce: u32,
|
||||
num_test_packets: u32,
|
||||
) -> Result<Vec<PreparedFragment>, NodeTesterError> {
|
||||
) -> Result<Vec<PreparedFragment>, WasmClientError> {
|
||||
let test_ext = WasmTestMessageExt::new(test_nonce);
|
||||
let mut tester_permit = self.tester.lock().expect("mutex got poisoned");
|
||||
tester_permit
|
||||
@@ -318,7 +330,7 @@ impl NymNodeTester {
|
||||
|
||||
// mark start of the test
|
||||
if self.test_in_progress.swap(true, Ordering::SeqCst) {
|
||||
return NodeTesterError::TestInProgress.into_rejected_promise();
|
||||
return WasmClientError::TestInProgress.into_rejected_promise();
|
||||
}
|
||||
|
||||
// prepare test packets
|
||||
@@ -0,0 +1,267 @@
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn validate_recipient(recipient: String) -> Result<(), JsError> {
|
||||
match Recipient::try_from_base58_string(recipient) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(JsError::new(format!("{}", e).as_str())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::validate_recipient;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_recipient_validation_ok() {
|
||||
let res = validate_recipient("DyQmXnst5NGGjzUZxRC5Bjs5bd7CBF3xMpsSmbRiizr2.GH6YTBP2NXU3AVqd8WjiTMVyeMjunXMEsp7gVCMEJqpD@336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9".to_string());
|
||||
assert!(res.is_ok())
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_recipient_validation_fails() {
|
||||
assert!(validate_recipient(" DyQmXnst5NGGjzUZxRC5Bjs5bd7CBF3xMpsSmbRiizr2.GH6YTBP2NXU3AVqd8WjiTMVyeMjunXMEsp7gVCMEJqpD@336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9".to_string()).is_err());
|
||||
assert!(validate_recipient(
|
||||
"DyQmXnst5NGGjzUZxRC5BjbRiizr2.GH6YTBP2NXU3AVqd8WD@336yuXAeGEgedRfqTJZQH1bHv1SjCZYarc9"
|
||||
.to_string()
|
||||
)
|
||||
.is_err());
|
||||
assert!(validate_recipient("🙀🙀🙀🙀".to_string()).is_err());
|
||||
assert!(validate_recipient("".to_string()).is_err());
|
||||
assert!(validate_recipient(" ".to_string()).is_err());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
extern crate wasm_bindgen_test;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
assert_eq!(1 + 1, 2);
|
||||
}
|
||||
@@ -22,11 +22,11 @@ serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = "0.10.6"
|
||||
tap = "1.0.1"
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tungstenite = { version = "0.13.0", default-features = false }
|
||||
tokio = { workspace = true, features = ["macros"]}
|
||||
thiserror = "1.0.34"
|
||||
time = "0.3.17"
|
||||
tokio = { version = "1.24.1", features = ["macros"]}
|
||||
tungstenite = { version = "0.13.0", default-features = false }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
# internal
|
||||
@@ -40,7 +40,7 @@ nym-gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
nym-topology = { path = "../topology", features = ["serializable"] }
|
||||
nym-topology = { path = "../topology" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-task = { path = "../task" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
@@ -51,7 +51,7 @@ version = "0.1.11"
|
||||
features = ["time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
workspace = true
|
||||
version = "1.24.1"
|
||||
features = ["time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
@@ -63,10 +63,10 @@ features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
optional = true
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
|
||||
workspace = true
|
||||
version = "0.4"
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
workspace = true
|
||||
version = "0.2.83"
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
workspace = true
|
||||
@@ -77,7 +77,7 @@ version = "0.2.4"
|
||||
features = ["futures"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
|
||||
path = "../wasm/utils"
|
||||
path = "../wasm-utils"
|
||||
features = ["websocket"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.time]
|
||||
@@ -88,7 +88,7 @@ features = ["wasm-bindgen"]
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
|
||||
[features]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user