Compare commits

..

1 Commits

Author SHA1 Message Date
serinko 7686c60333 DOC: smoosh-faq.md - quotation syntax fix 2023-09-19 16:46:43 +00:00
337 changed files with 6231 additions and 11756 deletions
@@ -1,4 +1,4 @@
name: ci-build-upload-binaries
name: Build and upload binaries to CI
on:
workflow_dispatch:
@@ -6,6 +6,7 @@ on:
paths:
- 'clients/**'
- 'common/**'
- 'contracts/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
@@ -20,6 +21,7 @@ on:
paths:
- 'clients/**'
- 'common/**'
- 'contracts/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
@@ -42,8 +44,6 @@ jobs:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
# a push event from the origin repo, or a PR from external repo
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'nymtech/nym' }}
steps:
- uses: actions/checkout@v3
@@ -63,13 +63,27 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.69.0
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release
args: --workspace --release --all
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.69.0
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install --version 0.112.0 wasm-opt
- name: Build release contracts
run: make contracts-wasm
- name: Prepare build output
shell: bash
@@ -85,6 +99,16 @@ jobs:
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ephemera.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
@@ -1,4 +1,4 @@
name: ci-build-ts
name: CI for ts-packages
on:
push:
@@ -1,4 +1,4 @@
name: ci-build
name: Continuous integration
on:
push:
@@ -42,8 +42,8 @@ jobs:
build:
runs-on: [ self-hosted, custom-linux ]
# Enable sccache via environment variable
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
@@ -91,14 +91,14 @@ jobs:
command: test
args: --workspace -- --ignored
- name: Annotate with clippy checks
uses: actions-rs/clippy-check@v1
- uses: actions-rs/clippy-check@v1
name: Clippy checks
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
- name: Clippy
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
@@ -0,0 +1,19 @@
[
{
"os":"ubuntu-20.04",
"rust":"stable",
"runOnEvent":"always"
},
{
"os":"windows-latest",
"rust":"stable",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"pull_request"
}
]
@@ -1,7 +1,6 @@
name: ci-contracts-schema
name: Check Contract Schema
on:
workflow_dispatch:
push:
paths:
- 'contracts/**'
@@ -1,4 +1,4 @@
name: nightly-check-merge-conflicts
name: check-merge-conflicts
# Check that the latest release branch merges into master and develop without
# any conflicts that git is not able to resolve
@@ -1,79 +0,0 @@
name: ci-contracts-upload-binaries
on:
workflow_dispatch:
push:
paths:
- 'common/**'
- 'contracts/**'
pull_request:
paths:
- 'common/**'
- 'contracts/**'
env:
NETWORK: mainnet
jobs:
publish-nym-contracts:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Prepare build output directory
shell: bash
env:
OUTPUT_DIR: ci-contract-builds/${{ github.ref_name }}
run: |
rm -rf ci-contract-builds || true
mkdir -p $OUTPUT_DIR
echo $OUTPUT_DIR
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.69.0
target: wasm32-unknown-unknown
override: true
- name: Install wasm-opt
run: cargo install --version 0.112.0 wasm-opt
- name: Build release contracts
run: make contracts
- name: Prepare build output
shell: bash
env:
OUTPUT_DIR: ci-contract-builds/${{ github.ref_name }}
run: |
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ephemera.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "ci-contract-builds/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
EXCLUDE: "/dist/, /node_modules/"
+1 -1
View File
@@ -1,4 +1,4 @@
name: ci-docs
name: CI docs
on:
workflow_dispatch:
@@ -1,4 +1,4 @@
name: ci-nym-connect-desktop
name: CI for nym-connect - Desktop
on:
push:
@@ -1,4 +1,4 @@
name: ci-nym-connect-desktop-rust
name: Nym Connect - desktop (Rust)
on:
push:
@@ -25,8 +25,8 @@ on:
jobs:
build:
runs-on: [self-hosted, custom-linux]
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
@@ -6,5 +6,9 @@
{
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"rust":"nightly",
"runOnEvent":"pull_request"
}
]
]
@@ -10,7 +10,7 @@ jobs:
runs-on: [self-hosted, custom-runner-linux]
steps:
- uses: actions/checkout@v2
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -23,7 +23,7 @@ jobs:
run: cargo install --version 0.112.0 wasm-opt
- name: Build release contracts
run: make contracts
run: make contracts-wasm
- name: Upload Mixnet Contract Artifact
uses: actions/upload-artifact@v3
@@ -1,4 +1,4 @@
name: ci-contracts
name: Contracts
on:
push:
@@ -6,7 +6,7 @@ on:
- 'contracts/**'
- 'common/**'
pull_request:
paths:
paths-ignore:
- 'contracts/**'
- 'common/**'
@@ -16,17 +16,18 @@ jobs:
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from ci-contracts-matrix-includes.json
# creates the matrix strategy from build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/ci-contracts-matrix-includes.json'
inputFile: '.github/workflows/contract_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
contracts:
# since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es
runs-on: ubuntu-20.04
continue-on-error: ${{ matrix.rust == 'nightly' }}
needs: matrix_prep
strategy:
fail-fast: false
@@ -34,8 +35,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Setup rust
uses: actions-rs/toolchain@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
@@ -43,28 +43,25 @@ jobs:
override: true
components: rustfmt, clippy
- name: Build contracts
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
env:
RUSTFLAGS: '-C link-arg=-s'
with:
command: build
args: --manifest-path contracts/Cargo.toml --workspace --lib --target wasm32-unknown-unknown
- name: Run unit tests
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
with:
command: test
args: --lib --manifest-path contracts/Cargo.toml
- name: Check formatting
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path contracts/Cargo.toml --all -- --check
- name: Run clippy
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --lib --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings
@@ -1,4 +1,4 @@
name: ci-nym-network-explorer
name: CI for Network Explorer
on:
workflow_dispatch:
-102
View File
@@ -1,102 +0,0 @@
name: nightly-build
on:
workflow_dispatch:
schedule:
- cron: '14 1 * * *'
jobs:
build:
strategy:
fail-fast: false
matrix:
rust: [stable, beta]
os: [custom-linux, windows10, custom-runner-mac-m1]
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
if: matrix.os == 'custom-linux'
- name: Check out repository code
uses: actions/checkout@v3
- name: Install 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 binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Build examples
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples
- name: Run unit tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Run slow unit tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace -- --ignored
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets -- -D warnings
notification:
needs: build
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"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -1,101 +0,0 @@
name: nightly-nym-wallet-build
on:
workflow_dispatch:
schedule:
- cron: '14 1 * * *'
defaults:
run:
working-directory: nym-wallet
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [custom-linux, macos-latest, windows10]
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
if: matrix.os == 'custom-linux'
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
# There is an issue with 1.72.0 where clippy crashes on nym-wallet-types. Pin to 1.71.0 for now
toolchain: 1.71.0
override: true
components: rustfmt, clippy
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Unit tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Annotate with clippy warnings
uses: actions-rs/clippy-check@v1
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets -- -D warnings
notification:
needs: build
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-wallet-nightly-build"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+176
View File
@@ -0,0 +1,176 @@
name: Nightly builds
on:
schedule:
- cron: '14 1 * * *'
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`]'
build:
needs: 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 repository code
uses: actions/checkout@v3
- 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
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"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -0,0 +1,50 @@
[
{
"os":"ubuntu-20.04",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"windows10",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"ubuntu-20.04",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"windows10",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"ubuntu-20.04",
"rust":"nightly",
"runOnEvent":"schedule"
},
{
"os":"windows10",
"rust":"nightly",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"schedule"
}
]
+32
View File
@@ -0,0 +1,32 @@
name: Release Nym Wallet
on:
workflow_dispatch:
inputs:
nym_wallet_version:
description: 'The version of the Nym Wallet to release'
default: '1.0.x'
required: true
type: string
jobs:
create-release:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: Create release
uses: softprops/action-gh-release@v1
with:
body: >-
This is a pre-release
Download the wallet for your platform:
- [Linux](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_amd64_ubuntu20.04.AppImage)
- [MacOS](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_macos_11.dmg)
- [Windows](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_windows.msi)
prerelease: true
name: Nym Wallet v${{ inputs.nym_wallet_version}}
tag_name: nym-wallet-v${{ inputs.nym_wallet_version}}
+78
View File
@@ -0,0 +1,78 @@
name: Webdriverio tests for nym wallet
on:
push:
paths:
- "nym-wallet/**"
defaults:
run:
working-directory: nym-wallet
jobs:
test:
name: wallet tests
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Tauri dependencies
run: >
sudo apt-get update &&
sudo apt-get install -y
libgtk-3-dev
libgtksourceview-3.0-dev
webkit2gtk-4.0
libappindicator3-dev
webkit2gtk-driver
xvfb
continue-on-error: true
- name: Install minimal stable
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- name: Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install yarn for building application
run: yarn install
- name: Build application
run: yarn run webpack:build & yarn run tauri:build
- name: Check binary exists
run: |
cd target/release/
(test -f nym-wallet && echo nym binary exists) || echo wallet does not exist
- name: Install dependencies
run: yarn install
working-directory: nym-wallet/webdriver
- name: Remove existing user datafile
uses: JesseTG/rm@v1.0.2
with:
path: nym-wallet/webdriver/common/data/user-data.json
- name: Create user data json file
id: create-json
uses: jsdaniell/create-json@1.1.2
with:
name: "user-data.json"
json: ${{ secrets.WALLET_USERDATA }}
dir: "nym-wallet/webdriver/common/data/"
- name: Install tauri-driver
uses: actions-rs/cargo@v1
with:
command: install
args: tauri-driver
- name: Launch tests
run: xvfb-run yarn test:runall
working-directory: nym-wallet/webdriver
@@ -1,4 +1,4 @@
name: ci-sdk-docs-typescript
name: Typescript SDK docs
on:
push:
@@ -1,4 +1,4 @@
name: Publish Typescript SDK
name: Publish SDK to NPM
on:
workflow_dispatch:
@@ -28,10 +28,8 @@ jobs:
- name: Install dependencies
run: yarn
- name: Build WASM and Typescript SDK
run: yarn sdk:build
- name: Publish to NPM
- name: Build and publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
run: ./sdk/typescript/scripts/publish.sh
working-directory: ./sdk/typescript/packages/sdk
run: scripts/publish.sh
@@ -1,4 +1,4 @@
name: ci-lint-typescript
name: CI for linting Typescript
on:
push:
@@ -1,4 +1,4 @@
name: ci-nym-wallet-rust
name: Nym Wallet (rust)
on:
push:
@@ -17,8 +17,8 @@ on:
jobs:
build:
runs-on: [ self-hosted, custom-linux ]
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
@@ -1,4 +1,4 @@
name: ci-sdk-wasm
name: Wasm Client
on:
pull_request:
@@ -9,14 +9,10 @@ on:
jobs:
wasm:
runs-on: [custom-runner-linux]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: actions-rs/toolchain@v1
with:
profile: minimal
@@ -35,7 +31,7 @@ jobs:
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
run: cargo install wasm-opt
run: cargo install wasm-opt
- name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli
Generated
+265 -321
View File
@@ -690,9 +690,18 @@ dependencies = [
[[package]]
name = "atoi"
version = "2.0.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5"
dependencies = [
"num-traits",
]
[[package]]
name = "atoi"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e"
dependencies = [
"num-traits",
]
@@ -916,9 +925,6 @@ name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
dependencies = [
"serde",
]
[[package]]
name = "bitvec"
@@ -1828,15 +1834,30 @@ dependencies = [
"toml 0.5.11",
]
[[package]]
name = "crc"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23"
dependencies = [
"crc-catalog 1.1.1",
]
[[package]]
name = "crc"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
dependencies = [
"crc-catalog",
"crc-catalog 2.2.0",
]
[[package]]
name = "crc-catalog"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
[[package]]
name = "crc-catalog"
version = "2.2.0"
@@ -2326,9 +2347,19 @@ dependencies = [
[[package]]
name = "dashmap"
version = "5.5.3"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
dependencies = [
"cfg-if",
"num_cpus",
]
[[package]]
name = "dashmap"
version = "5.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d"
dependencies = [
"cfg-if",
"hashbrown 0.14.0",
@@ -2370,7 +2401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
dependencies = [
"const-oid",
"pem-rfc7468 0.6.0",
"pem-rfc7468",
"zeroize",
]
@@ -2381,7 +2412,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [
"const-oid",
"pem-rfc7468 0.7.0",
"zeroize",
]
@@ -2619,6 +2649,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dotenvy"
version = "0.15.7"
@@ -2737,9 +2773,6 @@ name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
dependencies = [
"serde",
]
[[package]]
name = "elliptic-curve"
@@ -2755,7 +2788,7 @@ dependencies = [
"generic-array 0.14.7",
"group 0.12.1",
"hkdf 0.12.3",
"pem-rfc7468 0.6.0",
"pem-rfc7468",
"pkcs8 0.9.0",
"rand_core 0.6.4",
"sec1 0.3.0",
@@ -2933,17 +2966,6 @@ dependencies = [
"libc",
]
[[package]]
name = "etcetera"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
dependencies = [
"cfg-if",
"home",
"windows-sys 0.48.0",
]
[[package]]
name = "event-listener"
version = "2.5.3"
@@ -3008,7 +3030,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.2.0-rc.10"
version = "1.2.0-rc.2"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -3169,12 +3191,13 @@ dependencies = [
[[package]]
name = "flume"
version = "0.11.0"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
dependencies = [
"futures-core",
"futures-sink",
"pin-project",
"spin 0.9.8",
]
@@ -3280,13 +3303,13 @@ dependencies = [
[[package]]
name = "futures-intrusive"
version = "0.5.0"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5"
dependencies = [
"futures-core",
"lock_api",
"parking_lot 0.12.1",
"parking_lot 0.11.2",
]
[[package]]
@@ -3635,6 +3658,15 @@ dependencies = [
"serde_json",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash 0.7.6",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -3663,6 +3695,15 @@ dependencies = [
"allocator-api2",
]
[[package]]
name = "hashlink"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
dependencies = [
"hashbrown 0.11.2",
]
[[package]]
name = "hashlink"
version = "0.8.3"
@@ -3941,7 +3982,7 @@ dependencies = [
"hyper-rustls",
"rustls-native-certs",
"tokio",
"tokio-rustls",
"tokio-rustls 0.22.0",
"tower-service",
"webpki 0.21.4",
]
@@ -3959,7 +4000,7 @@ dependencies = [
"rustls 0.19.1",
"rustls-native-certs",
"tokio",
"tokio-rustls",
"tokio-rustls 0.22.0",
"webpki 0.21.4",
"webpki-roots 0.21.1",
]
@@ -4451,9 +4492,6 @@ name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin 0.5.2",
]
[[package]]
name = "lazycell"
@@ -5301,9 +5339,9 @@ dependencies = [
[[package]]
name = "libsqlite3-sys"
version = "0.26.0"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
dependencies = [
"cc",
"pkg-config",
@@ -5585,7 +5623,7 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.2.0-rc.10"
version = "1.2.0-rc.2"
dependencies = [
"futures",
"js-sys",
@@ -5922,23 +5960,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.5",
"smallvec",
"zeroize",
]
[[package]]
name = "num-derive"
version = "0.3.3"
@@ -5960,17 +5981,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg 1.1.0",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.16"
@@ -6058,7 +6068,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"sqlx",
"sqlx 0.6.3",
"tap",
"tempfile",
"thiserror",
@@ -6254,7 +6264,7 @@ dependencies = [
"async-trait",
"base64 0.21.4",
"cfg-if",
"dashmap",
"dashmap 5.5.0",
"dirs 4.0.0",
"futures",
"gloo-timers",
@@ -6279,7 +6289,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.7",
"sqlx",
"sqlx 0.6.3",
"tap",
"tempfile",
"thiserror",
@@ -6298,7 +6308,7 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.2.0-rc.10"
version = "1.2.0-rc.2"
dependencies = [
"anyhow",
"futures",
@@ -6414,7 +6424,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"log",
"sqlx",
"sqlx 0.5.11",
"thiserror",
"tokio",
]
@@ -6552,7 +6562,7 @@ dependencies = [
"bs58 0.4.0",
"clap 4.3.21",
"colored",
"dashmap",
"dashmap 4.0.2",
"dirs 4.0.0",
"dotenvy",
"futures",
@@ -6569,7 +6579,6 @@ dependencies = [
"nym-mixnet-client",
"nym-mixnode-common",
"nym-network-defaults",
"nym-network-requester",
"nym-pemstore",
"nym-sphinx",
"nym-statistics-common",
@@ -6582,7 +6591,7 @@ dependencies = [
"rand 0.7.3",
"serde",
"serde_json",
"sqlx",
"sqlx 0.5.11",
"subtle-encoding",
"thiserror",
"tokio",
@@ -6870,7 +6879,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"sqlx",
"sqlx 0.6.3",
"tap",
"tempfile",
"thiserror",
@@ -6891,7 +6900,7 @@ dependencies = [
"pretty_env_logger",
"rocket",
"serde",
"sqlx",
"sqlx 0.5.11",
"thiserror",
"tokio",
]
@@ -6917,7 +6926,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.2.0-rc.10"
version = "1.2.0-rc.2"
dependencies = [
"futures",
"js-sys",
@@ -7106,7 +7115,6 @@ dependencies = [
name = "nym-socks5-client-core"
version = "0.1.0"
dependencies = [
"anyhow",
"dirs 4.0.0",
"futures",
"log",
@@ -7345,7 +7353,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"sqlx",
"sqlx 0.5.11",
"thiserror",
"tokio",
]
@@ -7375,7 +7383,6 @@ dependencies = [
"tokio",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasmtimer",
]
[[package]]
@@ -7511,7 +7518,7 @@ dependencies = [
[[package]]
name = "nym-wasm-sdk"
version = "1.2.0-rc.10"
version = "1.2.0-rc.2"
dependencies = [
"mix-fetch-wasm",
"nym-client-wasm",
@@ -7727,7 +7734,7 @@ checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1"
dependencies = [
"async-trait",
"crossbeam-channel",
"dashmap",
"dashmap 5.5.0",
"fnv",
"futures-channel",
"futures-executor",
@@ -7980,15 +7987,6 @@ dependencies = [
"base64ct",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
@@ -8087,17 +8085,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der 0.7.8",
"pkcs8 0.10.2",
"spki 0.7.2",
]
[[package]]
name = "pkcs8"
version = "0.9.0"
@@ -9117,28 +9104,6 @@ dependencies = [
"librocksdb-sys",
]
[[package]]
name = "rsa"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8"
dependencies = [
"byteorder",
"const-oid",
"digest 0.10.7",
"num-bigint-dig",
"num-integer",
"num-iter",
"num-traits",
"pkcs1",
"pkcs8 0.10.2",
"rand_core 0.6.4",
"signature 2.1.0",
"spki 0.7.2",
"subtle 2.4.1",
"zeroize",
]
[[package]]
name = "rtcp"
version = "0.7.2"
@@ -9181,15 +9146,16 @@ dependencies = [
[[package]]
name = "rusqlite"
version = "0.29.0"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
dependencies = [
"bitflags 2.4.0",
"bitflags 1.3.2",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"hashlink 0.7.0",
"libsqlite3-sys",
"memchr",
"smallvec",
]
@@ -10072,6 +10038,17 @@ dependencies = [
"der 0.7.8",
]
[[package]]
name = "sqlformat"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
dependencies = [
"itertools",
"nom",
"unicode_categories",
]
[[package]]
name = "sqlformat"
version = "0.2.1"
@@ -10085,204 +10062,175 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.7.2"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33"
checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826"
dependencies = [
"sqlx-core",
"sqlx-macros",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
"sqlx-core 0.5.13",
"sqlx-macros 0.5.13",
]
[[package]]
name = "sqlx"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188"
dependencies = [
"sqlx-core 0.6.3",
"sqlx-macros 0.6.3",
]
[[package]]
name = "sqlx-core"
version = "0.7.2"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d"
checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5"
dependencies = [
"ahash 0.8.3",
"atoi",
"ahash 0.7.6",
"atoi 0.4.0",
"bitflags 1.3.2",
"byteorder",
"bytes",
"chrono",
"crc",
"crc 2.1.0",
"crossbeam-queue",
"dotenvy",
"either",
"event-listener",
"futures-channel",
"futures-core",
"futures-intrusive",
"futures-io",
"futures-util",
"hashlink",
"hex",
"indexmap 2.0.0",
"log",
"memchr",
"once_cell",
"paste",
"percent-encoding",
"rustls 0.21.7",
"rustls-pemfile",
"serde",
"serde_json",
"sha2 0.10.7",
"smallvec",
"sqlformat",
"thiserror",
"tokio",
"tokio-stream",
"tracing",
"url",
"webpki-roots 0.24.0",
]
[[package]]
name = "sqlx-macros"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec"
dependencies = [
"proc-macro2",
"quote",
"sqlx-core",
"sqlx-macros-core",
"syn 1.0.109",
]
[[package]]
name = "sqlx-macros-core"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc"
dependencies = [
"dotenvy",
"either",
"heck 0.4.1",
"hex",
"once_cell",
"proc-macro2",
"quote",
"serde",
"serde_json",
"sha2 0.10.7",
"sqlx-core",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
"syn 1.0.109",
"tempfile",
"tokio",
"url",
]
[[package]]
name = "sqlx-mysql"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db"
dependencies = [
"atoi",
"base64 0.21.4",
"bitflags 2.4.0",
"byteorder",
"bytes",
"chrono",
"crc",
"digest 0.10.7",
"dotenvy",
"either",
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"generic-array 0.14.7",
"hex",
"hkdf 0.12.3",
"hmac 0.12.1",
"itoa",
"log",
"md-5",
"memchr",
"once_cell",
"percent-encoding",
"rand 0.8.5",
"rsa",
"serde",
"sha1",
"sha2 0.10.7",
"smallvec",
"sqlx-core",
"stringprep",
"thiserror",
"tracing",
"whoami",
]
[[package]]
name = "sqlx-postgres"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624"
dependencies = [
"atoi",
"base64 0.21.4",
"bitflags 2.4.0",
"byteorder",
"chrono",
"crc",
"dotenvy",
"etcetera",
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"hex",
"hkdf 0.12.3",
"hmac 0.12.1",
"home",
"itoa",
"log",
"md-5",
"memchr",
"once_cell",
"rand 0.8.5",
"serde",
"serde_json",
"sha1",
"sha2 0.10.7",
"smallvec",
"sqlx-core",
"stringprep",
"thiserror",
"tracing",
"whoami",
]
[[package]]
name = "sqlx-sqlite"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f"
dependencies = [
"atoi",
"chrono",
"flume",
"futures-channel",
"futures-core",
"futures-executor",
"futures-intrusive",
"futures-util",
"hashlink 0.7.0",
"hex",
"indexmap 1.9.3",
"itoa",
"libc",
"libsqlite3-sys",
"log",
"memchr",
"once_cell",
"paste",
"percent-encoding",
"serde",
"sqlx-core",
"tracing",
"rustls 0.19.1",
"sha2 0.10.7",
"smallvec",
"sqlformat 0.1.8",
"sqlx-rt 0.5.13",
"stringprep",
"thiserror",
"tokio-stream",
"url",
"webpki 0.21.4",
"webpki-roots 0.21.1",
]
[[package]]
name = "sqlx-core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029"
dependencies = [
"ahash 0.7.6",
"atoi 1.0.0",
"bitflags 1.3.2",
"byteorder",
"bytes",
"chrono",
"crc 3.0.1",
"crossbeam-queue",
"dotenvy",
"either",
"event-listener",
"flume",
"futures-channel",
"futures-core",
"futures-executor",
"futures-intrusive",
"futures-util",
"hashlink 0.8.3",
"hex",
"indexmap 1.9.3",
"itoa",
"libc",
"libsqlite3-sys",
"log",
"memchr",
"once_cell",
"paste",
"percent-encoding",
"rustls 0.20.8",
"rustls-pemfile",
"sha2 0.10.7",
"smallvec",
"sqlformat 0.2.1",
"sqlx-rt 0.6.3",
"stringprep",
"thiserror",
"tokio-stream",
"url",
"webpki-roots 0.22.6",
]
[[package]]
name = "sqlx-macros"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
dependencies = [
"dotenv",
"either",
"heck 0.4.1",
"once_cell",
"proc-macro2",
"quote",
"sha2 0.10.7",
"sqlx-core 0.5.13",
"sqlx-rt 0.5.13",
"syn 1.0.109",
"url",
]
[[package]]
name = "sqlx-macros"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9"
dependencies = [
"dotenvy",
"either",
"heck 0.4.1",
"once_cell",
"proc-macro2",
"quote",
"sha2 0.10.7",
"sqlx-core 0.6.3",
"sqlx-rt 0.6.3",
"syn 1.0.109",
"url",
]
[[package]]
name = "sqlx-rt"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae"
dependencies = [
"once_cell",
"tokio",
"tokio-rustls 0.22.0",
]
[[package]]
name = "sqlx-rt"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024"
dependencies = [
"once_cell",
"tokio",
"tokio-rustls 0.23.4",
]
[[package]]
@@ -10383,7 +10331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25"
dependencies = [
"base64 0.13.1",
"crc",
"crc 3.0.1",
"lazy_static",
"md-5",
"rand 0.8.5",
@@ -10838,6 +10786,17 @@ dependencies = [
"webpki 0.21.4",
]
[[package]]
name = "tokio-rustls"
version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
dependencies = [
"rustls 0.20.8",
"tokio",
"webpki 0.22.0",
]
[[package]]
name = "tokio-socks"
version = "0.5.1"
@@ -11981,15 +11940,6 @@ dependencies = [
"rustls-webpki 0.100.2",
]
[[package]]
name = "webpki-roots"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888"
dependencies = [
"rustls-webpki 0.101.4",
]
[[package]]
name = "webrtc"
version = "0.6.0"
@@ -12095,7 +12045,7 @@ checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80"
dependencies = [
"arc-swap",
"async-trait",
"crc",
"crc 3.0.1",
"log",
"rand 0.8.5",
"serde",
@@ -12146,7 +12096,7 @@ dependencies = [
"arc-swap",
"async-trait",
"bytes",
"crc",
"crc 3.0.1",
"log",
"rand 0.8.5",
"thiserror",
@@ -12210,12 +12160,6 @@ dependencies = [
"once_cell",
]
[[package]]
name = "whoami"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50"
[[package]]
name = "widestring"
version = "0.5.1"
-25
View File
@@ -146,7 +146,6 @@ cw2 = { version = "=1.1.0" }
cw3 = { version = "=1.1.0" }
cw4 = { version = "=1.1.0" }
cw-controllers = { version = "=1.1.0" }
dashmap = "5.5.3"
dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7"
@@ -155,12 +154,10 @@ k256 = "0.13"
lazy_static = "1.4.0"
log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = "0.11.18"
serde = "1.0.152"
serde_json = "1.0.91"
sqlx = "0.7.2"
tap = "1.0.1"
tendermint-rpc = "0.32" # same version as used by cosmrs
thiserror = "1.0.38"
@@ -178,25 +175,3 @@ wasm-bindgen = "0.2.86"
wasm-bindgen-futures = "0.4.37"
wasmtimer = "0.2.0"
web-sys = "0.3.63"
# Profile settings for individual crates
[profile.release.package.nym-socks5-listener]
strip = true
codegen-units = 1
[profile.release.package.nym-client-wasm]
# lto = true
opt-level = 'z'
[profile.release.package.nym-node-tester-wasm]
# lto = true
opt-level = 'z'
[profile.release.package.nym-wasm-sdk]
# lto = true
opt-level = 'z'
[profile.release.package.mix-fetch-wasm]
# lto = true
opt-level = 'z'
+124 -106
View File
@@ -1,56 +1,42 @@
# Top-level Makefile for the nym monorepo
# Default target. Probably what you want to run in normal day-to-day usage when
# you want to check all backend code in one step.
# Default target
all: test
help:
@echo "The main targets are"
@echo " all: the default target. Alias for test"
@echo " build: build all binaries"
@echo " build-release: build platform binaries and contracts in release mode"
@echo " clippy: run clippy for all workspaces"
@echo " test: run clippy, unit tests, and formatting."
@echo " test-all: like test, but also includes the expensive tests"
test: clippy-all cargo-test contracts-wasm sdk-wasm-test fmt
# -----------------------------------------------------------------------------
# Meta targets
# -----------------------------------------------------------------------------
# Run clippy for all workspaces, run all tests, format all Rust code
test: clippy cargo-test fmt
# Same as test, but also runs slow tests
test-all: test cargo-test-expensive
# Build release binaries for the main workspace (platform binaries) and the
# contracts, including running wasm-opt.
# Producing release versions of other components is deferred to their
# respective toolchains.
build-release: build-release-main contracts
no-clippy: build cargo-test contracts-wasm fmt fmt-browser-extension-storage
# Not a meta target, more of a top-level target for building all binaries (in
# debug mode). Listed here for visibility. The deps are appended successively
build:
happy: fmt clippy-happy test
# Not a meta target, more of a top-level target for clippy. Listed here for
# visibility. The deps are appended successively.
clippy:
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
# Deprecated
# For backwards compatibility
clippy-all: clippy
# -----------------------------------------------------------------------------
# Define targets for a given workspace
# $(1): name
# $(2): path to workspace
# $(3): extra arguments to cargo
# $(4): RUSTFLAGS prefix env
# -----------------------------------------------------------------------------
define add_cargo_workspace
clippy-$(1):
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
clippy-happy-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml $(3)
clippy-extra-$(1):
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace --examples --tests -- -D warnings
clippy-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
clippy-examples-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml --workspace --examples -- -D warnings
check-$(1):
cargo check --manifest-path $(2)/Cargo.toml --workspace $(3)
@@ -61,25 +47,30 @@ 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)
build-extra-$(1):
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples --tests
build-examples-$(1):
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples
build-release-$(1):
$(4) cargo $$($(1)_BUILD_RELEASE_TOOLCHAIN) build --manifest-path $(2)/Cargo.toml --workspace --release $(3)
cargo build --manifest-path $(2)/Cargo.toml --workspace --release $(3)
fmt-$(1):
cargo fmt --manifest-path $(2)/Cargo.toml --all
clippy: clippy-$(1) clippy-extra-$(1)
clippy-happy: clippy-happy-$(1)
clippy: clippy-$(1) clippy-examples-$(1)
check: check-$(1)
cargo-test: test-$(1)
cargo-test-expensive: test-expensive-$(1)
build: build-$(1) build-extra-$(1)
build: build-$(1) build-examples-$(1)
build-release-all: build-release-$(1)
fmt: fmt-$(1)
endef
# -----------------------------------------------------------------------------
@@ -89,72 +80,11 @@ endef
# Generate targets for the various cargo workspaces
$(eval $(call add_cargo_workspace,main,.))
$(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unknown-unknown,RUSTFLAGS='-C link-arg=-s'))
$(eval $(call add_cargo_workspace,wallet,nym-wallet))
$(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,wallet,nym-wallet,))
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
# OVERRIDE: there is an issue where clippy crashes on nym-wallet-types with the latest
# stable toolchain. So pin to 1.71.0 until that is resolved.
wallet_CLIPPY_TOOLCHAIN := +1.71.0
# OVERRIDE: wasm-opt fails if the binary has been built with the latest rustc.
# Pin to the last working version.
contracts_BUILD_RELEASE_TOOLCHAIN := +1.69.0
# -----------------------------------------------------------------------------
# SDK
# -----------------------------------------------------------------------------
sdk-wasm: sdk-wasm-build sdk-wasm-test sdk-wasm-lint
sdk-wasm-build:
$(MAKE) -C nym-browser-extension/storage wasm-pack
$(MAKE) -C wasm/client
$(MAKE) -C wasm/node-tester
$(MAKE) -C wasm/mix-fetch
$(MAKE) -C wasm/full-nym-wasm
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
sdk-typescript-build:
npx lerna run --scope @nymproject/sdk build --stream
npx lerna run --scope @nymproject/mix-fetch build --stream
npx lerna run --scope @nymproject/node-tester build --stream
yarn --cwd sdk/typescript/codegen/contract-clients build
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm nym-wasm-sdk
sdk-wasm-test:
#cargo test $(addprefix -p , $(WASM_CRATES)) --target wasm32-unknown-unknown -- -Dwarnings
sdk-wasm-lint:
cargo clippy $(addprefix -p , $(WASM_CRATES)) --target wasm32-unknown-unknown -- -Dwarnings
$(MAKE) -C wasm/mix-fetch check-fmt
# Add to top-level targets
build: sdk-wasm-build
cargo-test: sdk-wasm-test
clippy: sdk-wasm-lint
# -----------------------------------------------------------------------------
# Build contracts ready for deploy
# -----------------------------------------------------------------------------
CONTRACTS=vesting_contract mixnet_contract nym_service_provider_directory nym_name_service
CONTRACTS_WASM=$(addsuffix .wasm, $(CONTRACTS))
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
contracts: build-release-contracts wasm-opt-contracts
wasm-opt-contracts:
for contract in $(CONTRACTS_WASM); do \
wasm-opt --disable-sign-ext -Os $(CONTRACTS_OUT_DIR)/$$contract -o $(CONTRACTS_OUT_DIR)/$$contract; \
done
# Consider adding 's' to make plural consistent (beware: used in github workflow)
contract-schema:
$(MAKE) -C contracts schema
# -----------------------------------------------------------------------------
# Convenience targets for crates that are already part of the main workspace
# -----------------------------------------------------------------------------
@@ -165,14 +95,102 @@ 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
# -----------------------------------------------------------------------------
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
VESTING_CONTRACT=$(CONTRACTS_OUT_DIR)/vesting_contract.wasm
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
contracts-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 --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)
wasm-opt --disable-sign-ext -Os $(NAME_SERVICE_CONTRACT) -o $(NAME_SERVICE_CONTRACT)
contract-schema:
$(MAKE) -C contracts schema
# -----------------------------------------------------------------------------
# Misc
# -----------------------------------------------------------------------------
# NOTE: this seems deprecated an not needed anymore?
mixnet-opt: contracts-wasm
cd contracts/mixnet && make opt
generate-typescript:
cd tools/ts-rs-cli && cargo run && cd ../..
yarn types:lint:fix
run-api-tests:
cd nym-api/tests/functional_test && yarn test:qa
+1 -1
View File
@@ -67,7 +67,7 @@ pub struct Config {
}
impl NymConfigTemplate for Config {
fn template(&self) -> &'static str {
fn template() -> &'static str {
CONFIG_TEMPLATE
}
}
@@ -14,7 +14,7 @@ pub struct ClientPaths {
impl ClientPaths {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
ClientPaths {
common_paths: CommonClientPaths::new_base(base_data_directory),
common_paths: CommonClientPaths::new_default(base_data_directory),
}
}
}
+142 -5
View File
@@ -4,19 +4,28 @@
use crate::client::config::Config;
use crate::error::ClientError;
use crate::websocket;
use futures::channel::mpsc;
use log::*;
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
};
use nym_client_core::client::inbound_messages::InputMessage;
use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
};
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType;
use nym_task::TaskHandle;
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;
pub use nym_sphinx::receiver::ReconstructedMessage;
pub mod config;
@@ -83,7 +92,7 @@ impl SocketClient {
pub async fn run_socket_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
let shutdown = self.start_socket().await?;
let res = shutdown.wait_for_shutdown().await;
let res = shutdown.catch_interrupt().await;
log::info!("Stopping nym-client");
res
}
@@ -116,7 +125,7 @@ impl SocketClient {
Ok(base_client)
}
pub async fn start_socket(self) -> Result<TaskHandle, ClientError> {
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
if !self.config.socket.socket_type.is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
@@ -135,13 +144,141 @@ impl SocketClient {
client_output,
client_state,
&self_address,
started_client.task_handle.get_handle(),
started_client.task_manager.subscribe(),
packet_type,
);
info!("Client startup finished!");
info!("The address of this client is: {self_address}");
Ok(started_client.task_handle)
Ok(started_client.task_manager)
}
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
if self.config.socket.socket_type.is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
let base_builder = self.create_base_client_builder().await?;
let packet_type = self.config.base.debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
let address = started_client.address;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
// register our receiver
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us
client_output
.received_buffer_request_sender
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
reconstructed_sender,
))
.expect("the buffer request failed!");
Ok(DirectClient {
client_input,
_received_buffer_request_sender: client_output.received_buffer_request_sender,
reconstructed_receiver,
address,
shutdown_notifier: started_client.task_manager,
packet_type,
})
}
}
pub struct DirectClient {
client_input: ClientInput,
// make sure to not drop the channel
_received_buffer_request_sender: ReceivedBufferRequestSender,
reconstructed_receiver: ReconstructedMessagesReceiver,
address: Recipient,
// we need to keep reference to this guy otherwise things will start dropping
shutdown_notifier: TaskManager,
packet_type: PacketType,
}
impl DirectClient {
pub fn address(&self) -> &Recipient {
&self.address
}
pub fn signal_shutdown(&self) -> Result<(), SendError<()>> {
self.shutdown_notifier.signal_shutdown()
}
pub async fn wait_for_shutdown(&mut self) {
self.shutdown_notifier.wait_for_shutdown().await
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_regular_message(&mut self, recipient: Recipient, message: Vec<u8>) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_regular(recipient, message, lane, Some(self.packet_type));
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_anonymous_message(
&mut self,
recipient: Recipient,
message: Vec<u8>,
reply_surbs: u32,
) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_anonymous(
recipient,
message,
reply_surbs,
lane,
Some(self.packet_type),
);
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_reply(&mut self, recipient_tag: AnonymousSenderTag, message: Vec<u8>) {
let lane = TransmissionLane::General;
let input_msg =
InputMessage::new_reply(recipient_tag, message, lane, Some(self.packet_type));
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
/// Note: it waits for the first occurrence of messages being sent to ourselves. If you expect multiple
/// messages, you might have to call this function repeatedly.
// TODO: I guess this should really return something that `impl Stream<Item=ReconstructedMessage>`
pub async fn wait_for_messages(&mut self) -> Vec<ReconstructedMessage> {
use futures::StreamExt;
self.reconstructed_receiver
.next()
.await
.expect("buffer controller seems to have somehow died!")
}
}
+17 -26
View File
@@ -15,9 +15,8 @@ use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use nym_topology::NymTopology;
@@ -115,7 +114,7 @@ impl From<Init> for OverrideConfig {
#[derive(Debug, Serialize)]
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::types::InitResults,
client_core: nym_client_core::init::InitResults,
client_listening_port: u16,
client_address: String,
}
@@ -123,11 +122,7 @@ pub struct InitResults {
impl InitResults {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
client_core: nym_client_core::init::types::InitResults::new(
&config.base,
address,
gateway,
),
client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway),
client_listening_port: config.socket.listening_port,
client_address: address.to_string(),
}
@@ -167,7 +162,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
// re-registering if wanted.
let user_wants_force_register = args.force_register_gateway;
if user_wants_force_register {
eprintln!("Instructed to force registering gateway. This will overwrite keys!");
eprintln!("Instructed to force registering gateway. This might overwrite keys!");
}
// If the client was already initialized, don't generate new keys and don't re-register with
@@ -177,10 +172,9 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway;
let selection_spec = GatewaySelectionSpecification::new(
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
false,
);
// Load and potentially override config
@@ -192,7 +186,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let available_gateways = if let Some(hardcoded_topology) = args
let network_gateways = if let Some(hardcoded_topology) = args
.custom_mixnet
.map(NymTopology::new_from_file)
.transpose()?
@@ -204,16 +198,16 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
current_gateways(&mut rng, &config.base.client.nym_api_urls).await?
};
let gateway_setup = GatewaySetup::New {
specification: selection_spec,
available_gateways,
overwrite_data: register_gateway,
};
let init_details =
nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
let init_details = nym_client_core::init::setup_gateway_from(
gateway_setup,
&key_store,
&details_store,
register_gateway,
Some(&network_gateways),
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?
.details;
let config_save_location = config.default_location();
config.save_to_default_location().tap_err(|_| {
@@ -228,10 +222,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
eprintln!("Client configuration completed.\n");
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
};
let init_results = InitResults::new(&config, &address, &gateway_details);
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
println!("{}", args.output.format(&init_results));
Ok(())
+1 -1
View File
@@ -133,7 +133,7 @@ fn persist_gateway_details(
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
+16 -21
View File
@@ -14,9 +14,8 @@ use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use nym_topology::NymTopology;
@@ -114,7 +113,7 @@ impl From<Init> for OverrideConfig {
#[derive(Debug, Serialize)]
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::types::InitResults,
client_core: nym_client_core::init::InitResults,
socks5_listening_port: u16,
client_address: String,
}
@@ -122,7 +121,7 @@ pub struct InitResults {
impl InitResults {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
client_core: nym_client_core::init::types::InitResults::new(
client_core: nym_client_core::init::InitResults::new(
&config.core.base,
address,
gateway,
@@ -177,10 +176,9 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway;
let selection_spec = GatewaySelectionSpecification::new(
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
false,
);
// Load and potentially override config
@@ -195,7 +193,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let available_gateways = if let Some(hardcoded_topology) = args
let network_gateways = if let Some(hardcoded_topology) = args
.custom_mixnet
.map(NymTopology::new_from_file)
.transpose()?
@@ -207,16 +205,16 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
current_gateways(&mut rng, &config.core.base.client.nym_api_urls).await?
};
let gateway_setup = GatewaySetup::New {
specification: selection_spec,
available_gateways,
overwrite_data: register_gateway,
};
let init_details =
nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
let init_details = nym_client_core::init::setup_gateway_from(
gateway_setup,
&key_store,
&details_store,
register_gateway,
Some(&network_gateways),
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?
.details;
// TODO: ask the service provider we specified for its interface version and set it in the config
@@ -231,10 +229,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
let address = init_details.client_address()?;
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
};
let init_results = InitResults::new(&config, &address, &gateway_details);
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
println!("{}", args.output.format(&init_results));
Ok(())
+1 -1
View File
@@ -175,7 +175,7 @@ fn persist_gateway_details(
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
+1 -1
View File
@@ -62,7 +62,7 @@ pub struct Config {
}
impl NymConfigTemplate for Config {
fn template(&self) -> &'static str {
fn template() -> &'static str {
CONFIG_TEMPLATE
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ pub struct SocksClientPaths {
impl SocksClientPaths {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
SocksClientPaths {
common_paths: CommonClientPaths::new_base(base_data_directory),
common_paths: CommonClientPaths::new_default(base_data_directory),
}
}
}
@@ -32,14 +32,4 @@ impl OutputFormat {
OutputFormat::Json => serde_json::to_string(data).unwrap(),
}
}
#[cfg(feature = "output_format")]
pub fn to_stdout<T: serde::Serialize + ToString>(&self, data: &T) {
println!("{}", self.format(data))
}
#[cfg(feature = "output_format")]
pub fn to_stderr<T: serde::Serialize + ToString>(&self, data: &T) {
eprintln!("{}", self.format(data))
}
}
+4 -3
View File
@@ -11,7 +11,7 @@ rust-version = "1.66"
async-trait = { workspace = true }
base64 = "0.21.2"
cfg-if = "1.0.0"
dashmap = { workspace = true }
dashmap = "5.4.0"
dirs = "4.0"
futures = { workspace = true }
humantime-serde = "1.0"
@@ -35,6 +35,7 @@ nym-config = { path = "../config" }
nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
#gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" }
@@ -57,7 +58,7 @@ features = ["time"]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
version = "0.6.2"
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
@@ -88,7 +89,7 @@ tempfile = "3.1.0"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
default = []
+89 -170
View File
@@ -8,7 +8,6 @@ use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::real_messages_control;
use crate::client::real_messages_control::RealMessagesController;
@@ -26,18 +25,16 @@ use crate::client::topology_control::{
};
use crate::config::{Config, DebugConfig};
use crate::error::ClientCoreError;
use crate::init::{
setup_gateway,
types::{GatewayDetails, GatewaySetup, InitialisationResult},
};
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult};
use crate::{config, spawn_future};
use futures::channel::mpsc;
use log::{debug, error, info};
use log::{debug, info};
use nym_bandwidth_controller::BandwidthController;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::encryption;
use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_client::{
AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver, PacketRouter,
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender,
};
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
@@ -45,11 +42,10 @@ use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_sphinx::params::PacketType;
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::{TaskClient, TaskHandle};
use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug;
use std::path::Path;
use std::sync::Arc;
use url::Url;
@@ -161,12 +157,7 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
config: &'a Config,
client_store: S,
dkg_query_client: Option<C>,
wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
shutdown: Option<TaskClient>,
setup_method: GatewaySetup,
}
@@ -184,10 +175,7 @@ where
config: base_config,
client_store,
dkg_query_client,
wait_for_gateway: false,
custom_topology_provider: None,
custom_gateway_transceiver: None,
shutdown: None,
setup_method: GatewaySetup::MustLoad,
}
}
@@ -198,12 +186,6 @@ where
self
}
#[must_use]
pub fn with_wait_for_gateway(mut self, wait_for_gateway: bool) -> Self {
self.wait_for_gateway = wait_for_gateway;
self
}
#[must_use]
pub fn with_topology_provider(
mut self,
@@ -213,18 +195,6 @@ where
self
}
#[must_use]
pub fn with_gateway_transceiver(mut self, sender: Box<dyn GatewayTransceiver + Send>) -> Self {
self.custom_gateway_transceiver = Some(sender);
self
}
#[must_use]
pub fn with_shutdown(mut self, shutdown: TaskClient) -> Self {
self.shutdown = Some(shutdown);
self
}
pub fn with_stored_topology<P: AsRef<Path>>(
mut self,
file: P,
@@ -236,13 +206,13 @@ where
// note: do **NOT** make this method public as its only valid usage is from within `start_base`
// because it relies on the crypto keys being already loaded
fn mix_address(details: &InitialisationResult) -> Recipient {
fn mix_address(details: &InitialisationDetails) -> Recipient {
Recipient::new(
*details.managed_keys.identity_public_key(),
*details.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(details.gateway_details.gateway_id()).unwrap(),
NodeIdentity::from_base58_string(&details.gateway_details.gateway_id).unwrap(),
)
}
@@ -329,37 +299,50 @@ where
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: TaskClient,
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
let managed_keys = initialisation_result.managed_keys;
let GatewayDetails::Configured(gateway_config) = initialisation_result.gateway_details
else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails);
};
let managed_keys = initialisation_result.details.managed_keys;
let mut gateway_client =
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client {
existing_client.upgrade(packet_router, bandwidth_controller, shutdown)
} else {
let cfg = gateway_config.try_into()?;
GatewayClient::new(
cfg,
managed_keys.identity_keypair(),
Some(managed_keys.must_get_gateway_shared_key()),
packet_router,
existing_client.upgrade(
mixnet_message_sender,
ack_sender,
config.debug.gateway_connection.gateway_response_timeout,
bandwidth_controller,
shutdown,
)
} else {
let gateway_config = initialisation_result.details.gateway_details;
let gateway_address = gateway_config.gateway_listener.clone();
let gateway_id = gateway_config.gateway_id;
// TODO: in theory, at this point, this should be infallible
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
GatewayClient::new(
gateway_address,
managed_keys.identity_keypair(),
gateway_identity,
Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender,
ack_sender,
config.debug.gateway_connection.gateway_response_timeout,
bandwidth_controller,
shutdown,
)
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
.with_response_timeout(config.debug.gateway_connection.gateway_response_timeout)
};
let gateway_id = gateway_client.gateway_identity();
gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
let shared_key = gateway_client
.authenticate_and_start()
@@ -372,48 +355,11 @@ where
}
})?;
managed_keys.ensure_gateway_key(Some(shared_key));
managed_keys.ensure_gateway_key(shared_key);
Ok(gateway_client)
}
async fn setup_gateway_transceiver(
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter,
mut shutdown: TaskClient,
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
// if we have setup custom gateway sender and persisted details agree with it, return it
if let Some(mut custom_gateway_transceiver) = custom_gateway_transceiver {
return if !initialisation_result.gateway_details.is_custom() {
Err(ClientCoreError::CustomGatewaySelectionExpected)
} else {
// and make sure to invalidate the task client so we wouldn't cause premature shutdown
shutdown.mark_as_success();
custom_gateway_transceiver.set_packet_router(packet_router)?;
Ok(custom_gateway_transceiver)
};
}
// otherwise, setup normal gateway client, etc
let gateway_client = Self::start_gateway_client(
config,
initialisation_result,
bandwidth_controller,
packet_router,
shutdown,
)
.await?;
Ok(Box::new(RemoteGateway::new(gateway_client)))
}
fn setup_topology_provider(
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
provider_from_config: config::TopologyStructure,
@@ -441,8 +387,6 @@ where
topology_provider: Box<dyn TopologyProvider + Send + Sync>,
topology_config: config::Topology,
topology_accessor: TopologyAccessor,
local_gateway: &NodeIdentity,
wait_for_gateway: bool,
mut shutdown: TaskClient,
) -> Result<(), ClientCoreError> {
let topology_refresher_config =
@@ -466,32 +410,6 @@ where
return Err(ClientCoreError::InsufficientNetworkTopology(err));
}
let gateway_wait_timeout = if wait_for_gateway {
Some(topology_config.max_startup_gateway_waiting_period)
} else {
None
};
if let Err(err) = topology_refresher
.ensure_contains_gateway(local_gateway)
.await
{
if let Some(waiting_timeout) = gateway_wait_timeout {
if let Err(err) = topology_refresher
.wait_for_gateway(local_gateway, waiting_timeout)
.await
{
error!(
"the gateway did not come back online within the specified timeout: {err}"
);
return Err(err.into());
}
} else {
error!("the gateway we're supposedly connected to does not exist. We'll not be able to send any packets to ourselves: {err}");
return Err(err.into());
}
}
if topology_config.disable_refreshing {
// if we're not spawning the refresher, don't cause shutdown immediately
info!("The topology refesher is not going to be started");
@@ -506,12 +424,19 @@ where
Ok(())
}
// controller for sending packets to mixnet (either real traffic or cover traffic)
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
gateway_client: GatewayClient<C, S::CredentialStore>,
shutdown: TaskClient,
) -> BatchMixMessageSender {
) -> BatchMixMessageSender
where
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_transceiver);
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
}
@@ -548,12 +473,21 @@ where
setup_method: GatewaySetup,
key_store: &S::KeyStore,
details_store: &S::GatewayDetailsStore,
overwrite_data: bool,
validator_servers: Option<&[Url]>,
) -> Result<InitialisationResult, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
{
setup_gateway(setup_method, key_store, details_store).await
setup_gateway(
setup_method,
key_store,
details_store,
overwrite_data,
validator_servers,
)
.await
}
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
@@ -571,11 +505,17 @@ where
self.setup_method,
self.client_store.key_store(),
self.client_store.gateway_details_store(),
false,
Some(&self.config.client.nym_api_urls),
)
.await?;
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
let bandwidth_controller = self
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
@@ -596,25 +536,31 @@ where
let shared_topology_accessor = TopologyAccessor::new();
// Shutdown notifier for signalling tasks to stop
let shutdown = self
.shutdown
.map(Into::<TaskHandle>::into)
.unwrap_or_default()
.name_if_unnamed("BaseNymClient");
let task_manager = TaskManager::default();
// channels responsible for dealing with reply-related fun
let (reply_controller_sender, reply_controller_receiver) =
reply_controller::requests::new_control_channels();
let self_address = Self::mix_address(&init_res);
let ack_key = init_res.managed_keys.ack_key();
let encryption_keys = init_res.managed_keys.encryption_keypair();
let self_address = Self::mix_address(&init_res.details);
let ack_key = init_res.details.managed_keys.ack_key();
let encryption_keys = init_res.details.managed_keys.encryption_keypair();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
let bandwidth_controller = self
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
let gateway_client = Self::start_gateway_client(
self.config,
init_res,
bandwidth_controller,
mixnet_messages_sender,
ack_sender,
task_manager.subscribe(),
)
.await?;
let reply_storage =
Self::setup_persistent_reply_storage(reply_storage_backend, task_manager.subscribe())
.await?;
let topology_provider = Self::setup_topology_provider(
self.custom_topology_provider.take(),
@@ -622,36 +568,11 @@ where
self.config.get_nym_api_endpoints(),
);
// needs to be started as the first thing to block if required waiting for the gateway
Self::start_topology_refresher(
topology_provider,
self.config.debug.topology,
shared_topology_accessor.clone(),
self_address.gateway(),
self.wait_for_gateway,
shutdown.fork("topology_refresher"),
)
.await?;
let gateway_packet_router = PacketRouter::new(
ack_sender,
mixnet_messages_sender,
shutdown.get_handle().named("gateway-packet-router"),
);
let gateway_transceiver = Self::setup_gateway_transceiver(
self.custom_gateway_transceiver,
self.config,
init_res,
bandwidth_controller,
gateway_packet_router,
shutdown.fork("gateway_transceiver"),
)
.await?;
let reply_storage = Self::setup_persistent_reply_storage(
reply_storage_backend,
shutdown.fork("persistent_reply_storage"),
task_manager.subscribe(),
)
.await?;
@@ -661,17 +582,15 @@ where
mixnet_messages_receiver,
reply_storage.key_storage(),
reply_controller_sender.clone(),
shutdown.fork("received_messages_buffer"),
task_manager.subscribe(),
);
// The message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let message_sender = Self::start_mix_traffic_controller(
gateway_transceiver,
shutdown.fork("mix_traffic_controller"),
);
let message_sender =
Self::start_mix_traffic_controller(gateway_client, task_manager.subscribe());
// Channels that the websocket listener can use to signal downstream to the real traffic
// controller that connections are closed.
@@ -698,7 +617,7 @@ where
reply_controller_receiver,
shared_lane_queue_lengths.clone(),
client_connection_rx,
shutdown.fork("real_traffic_controller"),
task_manager.subscribe(),
self.config.debug.traffic.packet_type,
);
@@ -714,7 +633,7 @@ where
self_address,
shared_topology_accessor.clone(),
message_sender,
shutdown.fork("cover_traffic_stream"),
task_manager.subscribe(),
);
}
@@ -739,7 +658,7 @@ where
reply_controller_sender,
topology_accessor: shared_topology_accessor,
},
task_handle: shutdown,
task_manager,
})
}
}
@@ -750,5 +669,5 @@ pub struct BaseClient {
pub client_output: ClientOutputStatus,
pub client_state: ClientState,
pub task_handle: TaskHandle,
pub task_manager: TaskManager,
}
@@ -2,12 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::types::{EmptyCustomDetails, GatewayDetails};
use async_trait::async_trait;
use log::error;
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;
@@ -17,61 +13,19 @@ use zeroize::Zeroizing;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewayDetailsStore<T = EmptyCustomDetails> {
pub trait GatewayDetailsStore {
type StorageError: Error;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails<T>, Self::StorageError>
where
T: DeserializeOwned + Send + Sync;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError>;
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails<T>,
) -> Result<(), Self::StorageError>
where
T: Serialize + Send + Sync;
details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PersistedGatewayDetails<T = EmptyCustomDetails> {
/// Standard details of a remote gateway
Default(PersistedGatewayConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(PersistedCustomGatewayDetails<T>),
}
impl<T> PersistedGatewayDetails<T> {
// TODO: this should probably allow for custom verification over T
pub fn validate(&self, shared_key: Option<&SharedKeys>) -> Result<(), ClientCoreError> {
match self {
PersistedGatewayDetails::Default(details) => {
if !details.verify(
shared_key
.ok_or(ClientCoreError::UnavailableSharedKey)?
.deref(),
) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
PersistedGatewayDetails::Custom(_) => {
if shared_key.is_some() {
error!("using custom persisted gateway setup with shared key present - are you sure that's what you want?");
// but technically we could still continue. just ignore the key
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PersistedGatewayConfig {
pub struct PersistedGatewayDetails {
// TODO: should we also verify correctness of the details themselves?
// i.e. we could include a checksum or tag (via the shared keys)
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
@@ -84,16 +38,13 @@ pub struct PersistedGatewayConfig {
pub(crate) details: GatewayEndpointConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistedCustomGatewayDetails<T> {
// whatever custom method is used, gateway's identity must be known
pub gateway_id: String,
#[serde(flatten)]
pub additional_data: T,
impl From<PersistedGatewayDetails> for GatewayEndpointConfig {
fn from(value: PersistedGatewayDetails) -> Self {
value.details
}
}
impl PersistedGatewayConfig {
impl PersistedGatewayDetails {
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
@@ -101,7 +52,7 @@ impl PersistedGatewayConfig {
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize().to_vec();
PersistedGatewayConfig { key_hash, details }
PersistedGatewayDetails { key_hash, details }
}
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
@@ -115,50 +66,6 @@ impl PersistedGatewayConfig {
}
}
impl<T> PersistedGatewayDetails<T> {
pub fn new(
details: GatewayDetails<T>,
shared_key: Option<&SharedKeys>,
) -> Result<Self, ClientCoreError> {
match details {
GatewayDetails::Configured(cfg) => {
let shared_key = shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?;
Ok(PersistedGatewayDetails::Default(
PersistedGatewayConfig::new(cfg, shared_key),
))
}
GatewayDetails::Custom(custom) => Ok(PersistedGatewayDetails::Custom(custom.into())),
}
}
pub fn is_custom(&self) -> bool {
matches!(self, PersistedGatewayDetails::Custom(..))
}
pub fn matches(&self, other: &GatewayDetails<T>) -> bool
where
T: PartialEq,
{
match self {
PersistedGatewayDetails::Default(default) => {
if let GatewayDetails::Configured(other_configured) = other {
&default.details == other_configured
} else {
false
}
}
PersistedGatewayDetails::Custom(custom) => {
if let GatewayDetails::Custom(other_custom) = other {
custom.gateway_id == other_custom.gateway_id
&& custom.additional_data == other_custom.additional_data
} else {
false
}
}
}
}
}
// helper to make Vec<u8> serialization use base64 representation to make it human readable
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
mod base64 {
@@ -209,10 +116,7 @@ impl OnDiskGatewayDetails {
}
}
pub fn load_from_disk<T>(&self) -> Result<PersistedGatewayDetails<T>, OnDiskGatewayDetailsError>
where
T: DeserializeOwned,
{
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
let file = std::fs::File::open(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::LoadFailure {
path: self.file_location.display().to_string(),
@@ -223,13 +127,10 @@ impl OnDiskGatewayDetails {
Ok(serde_json::from_reader(file)?)
}
pub fn store_to_disk<T>(
pub fn store_to_disk(
&self,
details: &PersistedGatewayDetails<T>,
) -> Result<(), OnDiskGatewayDetailsError>
where
T: Serialize,
{
details: &PersistedGatewayDetails,
) -> Result<(), OnDiskGatewayDetailsError> {
// ensure the whole directory structure exists
if let Some(parent_dir) = &self.file_location.parent() {
std::fs::create_dir_all(parent_dir).map_err(|err| {
@@ -269,8 +170,8 @@ impl GatewayDetailsStore for OnDiskGatewayDetails {
}
#[derive(Default)]
pub struct InMemGatewayDetails<T = EmptyCustomDetails> {
details: Mutex<Option<PersistedGatewayDetails<T>>>,
pub struct InMemGatewayDetails {
details: Mutex<Option<PersistedGatewayDetails>>,
}
#[derive(Debug, thiserror::Error)]
@@ -38,6 +38,9 @@ pub trait MixnetClientStorage {
type CredentialStore: CredentialStorage;
type GatewayDetailsStore: GatewayDetailsStore;
// this is a TERRIBLE name...
// fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore, Self::GatewayDetailsStore);
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
fn key_store(&self) -> &Self::KeyStore;
@@ -103,7 +103,7 @@ impl ManagedKeys {
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
match self {
ManagedKeys::Initial(_) => None,
ManagedKeys::FullyDerived(keys) => keys.gateway_shared_key(),
ManagedKeys::FullyDerived(keys) => Some(keys.gateway_shared_key()),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
@@ -124,26 +124,10 @@ impl ManagedKeys {
}
}
pub fn ensure_gateway_key(&self, gateway_shared_key: Option<Arc<SharedKeys>>) {
pub fn ensure_gateway_key(&self, gateway_shared_key: Arc<SharedKeys>) {
if let ManagedKeys::FullyDerived(key_manager) = &self {
if self.gateway_shared_key().is_none() && gateway_shared_key.is_none() {
// the key doesn't exist in either state
return;
}
if gateway_shared_key.is_some() && self.gateway_shared_key().is_none()
|| gateway_shared_key.is_none() && self.gateway_shared_key().is_some()
{
// if one is provided whilst the other is not...
// TODO: should this actually panic or return an error? would this branch be possible
// under normal operation?
panic!("inconsistent re-derived gateway key")
}
// here we know both keys MUST exist
let provided = gateway_shared_key.unwrap();
if !Arc::ptr_eq(key_manager.must_get_gateway_shared_key(), &provided)
|| *key_manager.must_get_gateway_shared_key() != provided
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|| key_manager.gateway_shared_key != gateway_shared_key
{
// this should NEVER happen thus panic here
panic!("derived fresh gateway shared key whilst already holding one!")
@@ -153,12 +137,12 @@ impl ManagedKeys {
pub async fn deal_with_gateway_key<S: KeyStore>(
&mut self,
gateway_shared_key: Option<Arc<SharedKeys>>,
gateway_shared_key: Arc<SharedKeys>,
key_store: &S,
) -> Result<(), S::StorageError> {
let key_manager = match std::mem::replace(self, ManagedKeys::Invalidated) {
ManagedKeys::Initial(keys) => {
let key_manager = keys.insert_maybe_gateway_shared_key(gateway_shared_key);
let key_manager = keys.insert_gateway_shared_key(gateway_shared_key);
key_manager.persist_keys(key_store).await?;
key_manager
}
@@ -200,10 +184,7 @@ impl KeyManagerBuilder {
}
}
pub fn insert_maybe_gateway_shared_key(
self,
gateway_shared_key: Option<Arc<SharedKeys>>,
) -> KeyManager {
pub fn insert_gateway_shared_key(self, gateway_shared_key: Arc<SharedKeys>) -> KeyManager {
KeyManager {
identity_keypair: self.identity_keypair,
encryption_keypair: self.encryption_keypair,
@@ -241,11 +222,7 @@ pub struct KeyManager {
encryption_keypair: Arc<encryption::KeyPair>,
/// shared key derived with the gateway during "registration handshake"
// I'm not a fan of how we broke the nice transition of `KeyManagerBuilder` -> `KeyManager`
// by making this field optional.
// However, it has to be optional for when we use embedded NR inside a gateway,
// since it won't have a shared key (because why would it?)
gateway_shared_key: Option<Arc<SharedKeys>>,
gateway_shared_key: Arc<SharedKeys>,
/// key used for producing and processing acknowledgement packets.
ack_key: Arc<AckKey>,
@@ -255,13 +232,13 @@ impl KeyManager {
pub fn from_keys(
id_keypair: identity::KeyPair,
enc_keypair: encryption::KeyPair,
gateway_shared_key: Option<SharedKeys>,
gateway_shared_key: SharedKeys,
ack_key: AckKey,
) -> Self {
Self {
identity_keypair: Arc::new(id_keypair),
encryption_keypair: Arc::new(enc_keypair),
gateway_shared_key: gateway_shared_key.map(Arc::new),
gateway_shared_key: Arc::new(gateway_shared_key),
ack_key: Arc::new(ack_key),
}
}
@@ -288,23 +265,13 @@ impl KeyManager {
Arc::clone(&self.ack_key)
}
fn must_get_gateway_shared_key(&self) -> &Arc<SharedKeys> {
self.gateway_shared_key
.as_ref()
.expect("gateway shared key is unavailable")
}
pub fn uses_custom_gateway(&self) -> bool {
self.gateway_shared_key.is_none()
}
/// Gets an atomically reference counted pointer to [`SharedKey`].
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
self.gateway_shared_key.as_ref().map(Arc::clone)
pub fn gateway_shared_key(&self) -> Arc<SharedKeys> {
Arc::clone(&self.gateway_shared_key)
}
pub fn remove_gateway_key(self) -> KeyManagerBuilder {
if Arc::strong_count(self.must_get_gateway_shared_key()) > 1 {
if Arc::strong_count(&self.gateway_shared_key) > 1 {
panic!("attempted to remove gateway key whilst still holding multiple references!")
}
KeyManagerBuilder {
@@ -88,20 +88,20 @@ impl OnDiskKeys {
pub fn ephemeral_load_gateway_keys(
&self,
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
self.load_key(self.paths.gateway_shared_key(), "gateway shared")
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
.map(zeroize::Zeroizing::new)
}
#[doc(hidden)]
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
let encryption_paths = self.paths.encryption_key_pair_path();
self.load_keypair(encryption_paths, "encryption")
self.load_keypair(encryption_paths, "encryption keys")
}
#[doc(hidden)]
pub fn load_identity_keypair(&self) -> Result<identity::KeyPair, OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path();
self.load_keypair(identity_paths, "identity")
self.load_keypair(identity_paths, "identity keys")
}
fn load_key<T: PemStorableKey>(
@@ -161,9 +161,8 @@ impl OnDiskKeys {
let encryption_keypair = self.load_encryption_keypair()?;
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
let gateway_shared_key: Option<SharedKeys> = self
.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
.ok();
let gateway_shared_key: SharedKeys =
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")?;
Ok(KeyManager::from_keys(
identity_keypair,
@@ -174,8 +173,6 @@ impl OnDiskKeys {
}
fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> {
use std::ops::Deref;
let identity_paths = self.paths.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path();
@@ -191,14 +188,11 @@ impl OnDiskKeys {
)?;
self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?;
if let Some(shared_keys) = &keys.gateway_shared_key {
self.store_key(
shared_keys.deref(),
self.paths.gateway_shared_key(),
"gateway shared keys",
)?;
}
self.store_key(
keys.gateway_shared_key.as_ref(),
self.paths.gateway_shared_key(),
"gateway shared keys",
)?;
Ok(())
}
@@ -1,26 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
use crate::spawn_future;
use log::*;
use nym_credential_storage::storage::Storage;
use nym_gateway_client::GatewayClient;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
pub mod transceiver;
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
const MAX_FAILURE_COUNT: usize = 100;
// that's also disgusting.
pub struct Empty;
pub struct MixTrafficController {
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
pub struct MixTrafficController<C, St: Storage> {
// TODO: most likely to be replaced by some higher level construct as
// later on gateway_client will need to be accessible by other entities
gateway_client: GatewayClient<C, St>,
mix_rx: BatchMixMessageReceiver,
// TODO: this is temporary work-around.
@@ -28,31 +26,20 @@ pub struct MixTrafficController {
consecutive_gateway_failure_count: usize,
}
impl MixTrafficController {
pub fn new<T>(gateway_transceiver: T) -> (MixTrafficController, BatchMixMessageSender)
where
T: GatewayTransceiver + Send + 'static,
{
impl<C, St> MixTrafficController<C, St>
where
C: DkgQueryClient + Sync + Send + 'static,
St: Storage + 'static,
<St as Storage>::StorageError: Send + Sync + 'static,
{
pub fn new(
gateway_client: GatewayClient<C, St>,
) -> (MixTrafficController<C, St>, BatchMixMessageSender) {
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
MixTrafficController {
gateway_transceiver: Box::new(gateway_transceiver),
mix_rx: message_receiver,
consecutive_gateway_failure_count: 0,
},
message_sender,
)
}
pub fn new_dynamic(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
) -> (MixTrafficController, BatchMixMessageSender) {
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
MixTrafficController {
gateway_transceiver,
gateway_client,
mix_rx: message_receiver,
consecutive_gateway_failure_count: 0,
},
@@ -65,16 +52,16 @@ impl MixTrafficController {
let result = if mix_packets.len() == 1 {
let mix_packet = mix_packets.pop().unwrap();
self.gateway_transceiver.send_mix_packet(mix_packet).await
self.gateway_client.send_mix_packet(mix_packet).await
} else {
self.gateway_transceiver
self.gateway_client
.batch_send_mix_packets(mix_packets)
.await
};
match result {
Err(err) => {
error!("Failed to send sphinx packet(s) to the gateway: {err}");
error!("Failed to send sphinx packet(s) to the gateway! - {err}");
self.consecutive_gateway_failure_count += 1;
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// todo: in the future this should initiate a 'graceful' shutdown or try
@@ -1,262 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use log::{debug, error};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient;
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
use nym_sphinx::forwarding::packet::MixPacket;
use std::fmt::Debug;
use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))]
use futures::channel::{mpsc, oneshot};
// we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch
#[derive(Debug, Error)]
#[error(transparent)]
pub struct ErasedGatewayError(Box<dyn std::error::Error + Send + Sync>);
fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGatewayError {
ErasedGatewayError(Box::new(err))
}
/// This combines combines the functionalities of being able to send and receive mix packets.
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
fn gateway_identity(&self) -> identity::PublicKey;
}
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
/// usually through a gateway.
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewaySender {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError>;
async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
// allow for optimisation when sending multiple packets
for packet in packets {
self.send_mix_packet(packet).await?;
}
Ok(())
}
}
/// this trait defines the functionality of being able to correctly route
/// packets received from the mixnet, i.e. acks and 'proper' messages.
pub trait GatewayReceiver {
// ughhhh I really dislike this method, but couldn't come up wih anything better
// ideally this would have been an associated type, but heh. we can't.
fn set_packet_router(
&mut self,
_packet_router: PacketRouter,
) -> Result<(), ErasedGatewayError> {
debug!("no-op packet router setup");
Ok(())
}
}
// to allow for dynamic dispatch
impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
#[inline]
fn gateway_identity(&self) -> identity::PublicKey {
(**self).gateway_identity()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<G: GatewaySender + ?Sized + Send> GatewaySender for Box<G> {
#[inline]
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
(**self).send_mix_packet(packet).await
}
#[inline]
async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
(**self).batch_send_mix_packets(packets).await
}
}
impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> {
#[inline]
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
(**self).set_packet_router(packet_router)
}
}
/// Gateway to which the client is connected through a socket.
/// Most likely through a websocket.
pub struct RemoteGateway<C, St> {
gateway_client: GatewayClient<C, St>,
}
impl<C, St> RemoteGateway<C, St> {
pub fn new(gateway_client: GatewayClient<C, St>) -> Self {
Self { gateway_client }
}
}
impl<C, St> GatewayTransceiver for RemoteGateway<C, St>
where
C: Send,
St: Send,
{
fn gateway_identity(&self) -> identity::PublicKey {
self.gateway_client.gateway_identity()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C, St> GatewaySender for RemoteGateway<C, St>
where
C: Send,
St: Send,
{
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.gateway_client
.send_mix_packet(packet)
.await
.map_err(erase_err)
}
async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
self.gateway_client
.batch_send_mix_packets(packets)
.await
.map_err(erase_err)
}
}
impl<C, St> GatewayReceiver for RemoteGateway<C, St> {}
#[derive(Debug, Error)]
pub enum LocalGatewayError {
#[error("attempted to set the packet router for the second time")]
PacketRouterAlreadySet,
#[error("failed to setup packet router - has the receiver been dropped?")]
FailedPacketRouterSetup,
}
/// Gateway running within the same process.
#[cfg(not(target_arch = "wasm32"))]
pub struct LocalGateway {
/// Identity of the locally managed gateway
local_identity: identity::PublicKey,
// 'sender' part
/// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers.
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
// 'receiver' part
packet_router_tx: Option<oneshot::Sender<PacketRouter>>,
}
#[cfg(not(target_arch = "wasm32"))]
impl LocalGateway {
pub fn new(
local_identity: identity::PublicKey,
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
packet_router_tx: oneshot::Sender<PacketRouter>,
) -> Self {
LocalGateway {
local_identity,
packet_forwarder,
packet_router_tx: Some(packet_router_tx),
}
}
}
#[cfg(not(target_arch = "wasm32"))]
mod nonwasm_sealed {
use super::*;
impl GatewayTransceiver for LocalGateway {
fn gateway_identity(&self) -> identity::PublicKey {
self.local_identity
}
}
#[async_trait]
impl GatewaySender for LocalGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.packet_forwarder
.unbounded_send(packet)
.map_err(|err| err.into_send_error())
.map_err(erase_err)
}
}
impl GatewayReceiver for LocalGateway {
fn set_packet_router(
&mut self,
packet_router: PacketRouter,
) -> Result<(), ErasedGatewayError> {
let Some(packet_routex_tx) = self.packet_router_tx.take() else {
return Err(erase_err(LocalGatewayError::PacketRouterAlreadySet));
};
packet_routex_tx
.send(packet_router)
.map_err(|_| erase_err(LocalGatewayError::FailedPacketRouterSetup))
}
}
}
// if we ever decided to start writing unit tests... : )
pub struct MockGateway {
dummy_identity: identity::PublicKey,
packet_router: Option<PacketRouter>,
sent: Vec<MixPacket>,
}
impl Default for MockGateway {
fn default() -> Self {
MockGateway {
dummy_identity: "3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7"
.parse()
.unwrap(),
packet_router: None,
sent: vec![],
}
}
}
#[derive(Debug, Error)]
#[error("mock gateway error")]
pub struct MockGatewayError;
impl GatewayReceiver for MockGateway {
// TODO: that's frustrating. can't do anything about the behaviour here since all the routing is in the `PacketRouter`...
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
self.packet_router = Some(packet_router);
Ok(())
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewaySender for MockGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.sent.push(packet);
Ok(())
}
}
impl GatewayTransceiver for MockGateway {
fn gateway_identity(&self) -> identity::PublicKey {
self.dummy_identity
}
}
@@ -71,7 +71,7 @@ impl AcknowledgementListener {
while !shutdown.is_shutdown() {
tokio::select! {
acks = self.ack_receiver.next() => match acks {
Some(acks) => self.handle_ack_receiver_item(acks).await,
Some(acks) => {self.handle_ack_receiver_item(acks).await}
None => {
log::trace!("AcknowledgementListener: Stopping since channel closed");
break;
@@ -260,7 +260,7 @@ where
let mut sent_notification_listener = self.sent_notification_listener;
let mut action_controller = self.action_controller;
let shutdown_handle = shutdown.fork("acknowledgement_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
acknowledgement_listener
.run_with_shutdown(shutdown_handle)
@@ -268,7 +268,7 @@ where
debug!("The acknowledgement listener has finished execution!");
});
let shutdown_handle = shutdown.fork("input_message_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
input_message_listener
.run_with_shutdown(shutdown_handle)
@@ -276,7 +276,7 @@ where
debug!("The input listener has finished execution!");
});
let shutdown_handle = shutdown.fork("retransmission_request_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
retransmission_request_listener
.run_with_shutdown(shutdown_handle, packet_type)
@@ -284,7 +284,7 @@ where
debug!("The retransmission request listener has finished execution!");
});
let shutdown_handle = shutdown.fork("sent_notification_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
sent_notification_listener
.run_with_shutdown(shutdown_handle)
@@ -293,9 +293,7 @@ where
});
spawn_future(async move {
action_controller
.run_with_shutdown(shutdown.with_suffix("action_controller"))
.await;
action_controller.run_with_shutdown(shutdown).await;
debug!("The controller has finished execution!");
});
}
@@ -213,17 +213,17 @@ impl RealMessagesController<OsRng> {
let ack_control = self.ack_control;
let mut reply_control = self.reply_control;
let shutdown_handle = shutdown.fork("out_queue_control");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
out_queue_control.run_with_shutdown(shutdown_handle).await;
debug!("The out queue controller has finished execution!");
});
let shutdown_handle = shutdown.fork("reply_control");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
reply_control.run_with_shutdown(shutdown_handle).await;
debug!("The reply controller has finished execution!");
});
ack_control.start_with_shutdown(shutdown.with_suffix("ack_control"), packet_type);
ack_control.start_with_shutdown(shutdown, packet_type);
}
}
@@ -500,7 +500,7 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver;
let shutdown_handle = shutdown.fork("fragmented_message_receiver");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
match fragmented_message_receiver
.run_with_shutdown(shutdown_handle)
@@ -511,9 +511,7 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
}
});
spawn_future(async move {
request_receiver
.run_with_shutdown(shutdown.with_suffix("request_receiver"))
.await;
request_receiver.run_with_shutdown(shutdown).await;
});
}
}
@@ -30,11 +30,11 @@ impl StorageManager {
})?;
}
let opts = sqlx::sqlite::SqliteConnectOptions::new()
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
.filename(database_path)
.create_if_missing(fresh);
let opts = opts.disable_statement_logging();
opts.disable_statement_logging();
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
Ok(pool) => pool,
@@ -5,17 +5,10 @@ use crate::spawn_future;
pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
use futures::StreamExt;
use log::*;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::NymTopologyError;
use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep;
mod accessor;
pub mod geo_aware_provider;
pub(crate) mod nym_api_provider;
@@ -93,54 +86,6 @@ impl TopologyRefresher {
self.topology_accessor.ensure_is_routable().await
}
pub async fn ensure_contains_gateway(
&self,
gateway: &NodeIdentity,
) -> Result<(), NymTopologyError> {
let topology = self
.topology_accessor
.current_topology()
.await
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
if !topology.gateway_exists(gateway) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: gateway.to_base58_string(),
});
}
Ok(())
}
pub async fn wait_for_gateway(
&mut self,
gateway: &NodeIdentity,
timeout_duration: Duration,
) -> Result<(), NymTopologyError> {
info!(
"going to wait for at most {timeout_duration:?} for gateway '{gateway}' to come online"
);
let deadline = sleep(timeout_duration);
tokio::pin!(deadline);
loop {
tokio::select! {
_ = &mut deadline => {
return Err(NymTopologyError::TimedOutWaitingForGateway {
identity_key: gateway.to_base58_string()
})
}
_ = self.try_refresh() => {
if self.ensure_contains_gateway(gateway).await.is_ok() {
return Ok(())
}
info!("gateway '{gateway}' is still not online...");
sleep(self.refresh_rate).await
}
}
}
}
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
@@ -35,7 +35,7 @@ pub struct ClientKeysPaths {
}
impl ClientKeysPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
ClientKeysPaths {
@@ -29,14 +29,14 @@ pub struct CommonClientPaths {
}
impl CommonClientPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
CommonClientPaths {
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
gateway_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
keys: ClientKeysPaths::new_base(base_data_directory),
keys: ClientKeysPaths::new_default(base_data_directory),
}
}
}
+5 -52
View File
@@ -3,7 +3,6 @@
use nym_config::defaults::NymNetworkDetails;
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::GatewayConfig;
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
@@ -30,8 +29,6 @@ const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20)
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
@@ -140,17 +137,6 @@ impl Config {
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
}
pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_poisson_process()
}
self
}
pub fn set_no_poisson_process(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic()
@@ -244,19 +230,6 @@ pub struct GatewayEndpointConfig {
pub gateway_listener: String,
}
impl TryFrom<GatewayEndpointConfig> for GatewayConfig {
type Error = ClientCoreError;
fn try_from(value: GatewayEndpointConfig) -> Result<Self, Self::Error> {
Ok(GatewayConfig {
gateway_identity: identity::PublicKey::from_base58_string(value.gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?,
gateway_owner: Some(value.gateway_owner),
gateway_listener: value.gateway_listener,
})
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
impl GatewayEndpointConfig {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
@@ -279,29 +252,15 @@ impl GatewayEndpointConfig {
identity::PublicKey::from_base58_string(&self.gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
}
}
pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self {
// TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct
if use_tls {
Self::from_topology_node_tls(node)
} else {
Self::from_topology_node_no_tls(node)
}
}
pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self {
impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
fn from(node: nym_topology::gateway::Node) -> GatewayEndpointConfig {
let gateway_listener = node.clients_address();
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address(),
gateway_owner: node.owner,
}
}
pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address_tls(),
gateway_owner: node.owner,
gateway_listener,
}
}
}
@@ -528,11 +487,6 @@ pub struct Topology {
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructure,
}
@@ -567,7 +521,6 @@ impl Default for Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructure::default(),
}
}
@@ -267,7 +267,7 @@ impl From<TopologyV1_1_20_2> for Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
..Default::default()
topology_structure: Default::default(),
}
}
}
+16 -20
View File
@@ -1,7 +1,6 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::ErasedGatewayError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError;
use nym_topology::gateway::GatewayConversionError;
@@ -20,12 +19,6 @@ pub enum ClientCoreError {
source: GatewayClientError,
},
#[error("Custom gateway client error: {source}")]
ErasedGatewayClientError {
#[from]
source: ErasedGatewayError,
},
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
@@ -38,9 +31,15 @@ pub enum ClientCoreError {
#[error("No gateways on network")]
NoGatewaysOnNetwork,
#[error("Failed to setup gateway")]
FailedToSetupGateway,
#[error("List of nym apis is empty")]
ListOfNymApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
#[error("The current network topology seem to be insufficient to route any packets through")]
InsufficientNetworkTopology(#[from] NymTopologyError),
@@ -62,6 +61,15 @@ pub enum ClientCoreError {
#[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
#[error("The identity of the gateway is unknown - did you run init?")]
GatewayIdUnknown,
#[error("The owner of the gateway is unknown - did you run init?")]
GatewayOwnerUnknown,
#[error("The address of the gateway is unknown - did you run init?")]
GatewayAddressUnknown,
#[error("The gateway is malformed: {source}")]
MalformedGateway {
#[from]
@@ -114,18 +122,6 @@ pub enum ClientCoreError {
#[error("unable to upgrade config file to `{new_version}`")]
UnableToUpgradeConfigFile { new_version: String },
#[error("the provided gateway details don't much the stored data")]
MismatchedStoredGatewayDetails,
#[error("custom selection of gateway was expected")]
CustomGatewaySelectionExpected,
#[error("the persisted gateway details were set for a custom setup")]
UnexpectedPersistedCustomGatewayDetails,
#[error("this client has performed gateway initialisation in another session")]
NoInitClientPresent,
}
/// Set of messages that the client can send to listeners via the task manager
+10 -43
View File
@@ -3,12 +3,12 @@
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::types::RegistrationResult;
use crate::init::RegistrationResult;
use futures::{SinkExt, StreamExt};
use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient;
use nym_topology::{filter::VersionFilterable, gateway, mix};
use nym_topology::{filter::VersionFilterable, gateway};
use rand::{seq::SliceRandom, Rng};
use std::{sync::Arc, time::Duration};
use tungstenite::Message;
@@ -24,7 +24,6 @@ use tokio_tungstenite::connect_async;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
use nym_validator_client::client::IdentityKeyRef;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
@@ -78,28 +77,6 @@ pub async fn current_gateways<R: Rng>(
Ok(filtered_gateways)
}
pub async fn current_mixnodes<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
) -> Result<Vec<mix::Node>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of mixnodes from: {nym_api}");
let mixnodes = client.get_cached_mixnodes().await?;
let valid_mixnodes = mixnodes
.into_iter()
.filter_map(|mixnode| (&mixnode.bond_information).try_into().ok())
.collect::<Vec<mix::Node>>();
// we were always filtering by version so I'm not removing that 'feature'
let filtered_mixnodes = valid_mixnodes.filter_by_version(env!("CARGO_PKG_VERSION"));
Ok(filtered_mixnodes)
}
#[cfg(not(target_arch = "wasm32"))]
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
@@ -220,27 +197,17 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
.cloned()
}
pub(super) fn get_specified_gateway(
gateway_identity: IdentityKeyRef,
gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
}
pub(super) async fn register_with_gateway(
gateway: &GatewayEndpointConfig,
our_identity: Arc<identity::KeyPair>,
) -> Result<RegistrationResult, ClientCoreError> {
let mut gateway_client =
GatewayClient::new_init(gateway.to_owned().try_into()?, our_identity.clone());
let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init(
gateway.gateway_listener.clone(),
gateway.try_get_gateway_identity_key()?,
our_identity.clone(),
timeout,
);
gateway_client.establish_connection().await.map_err(|err| {
log::warn!("Failed to establish connection with gateway!");
ClientCoreError::GatewayClientError {
@@ -263,6 +230,6 @@ pub(super) async fn register_with_gateway(
})?;
Ok(RegistrationResult {
shared_keys,
authenticated_ephemeral_client: gateway_client,
authenticated_ephemeral_client: Some(gateway_client),
})
}
+372 -139
View File
@@ -8,34 +8,246 @@ use crate::client::base_client::storage::gateway_details::{
};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys;
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::helpers::{
choose_gateway_by_latency, get_specified_gateway, uniformly_random_gateway,
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
use crate::{
config::{Config, GatewayEndpointConfig},
error::ClientCoreError,
};
use crate::init::types::{
CustomGatewayDetails, GatewayDetails, GatewaySelectionSpecification, GatewaySetup,
InitialisationResult,
};
use nym_gateway_client::client::InitGatewayClient;
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::InitOnly;
use nym_gateway_client::GatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fmt::{Debug, Display};
use std::sync::Arc;
use url::Url;
pub mod helpers;
pub mod types;
pub struct RegistrationResult {
pub shared_keys: Arc<SharedKeys>,
pub authenticated_ephemeral_client: Option<GatewayClient<InitOnly>>,
}
pub struct InitialisationResult {
pub details: InitialisationDetails,
pub authenticated_ephemeral_client: Option<GatewayClient<InitOnly>>,
}
impl From<InitialisationDetails> for InitialisationResult {
fn from(details: InitialisationDetails) -> Self {
InitialisationResult {
details,
authenticated_ephemeral_client: None,
}
}
}
// TODO: rename to something better...
#[derive(Debug)]
pub struct InitialisationDetails {
pub gateway_details: GatewayEndpointConfig,
pub managed_keys: ManagedKeys,
}
impl InitialisationDetails {
pub fn new(gateway_details: GatewayEndpointConfig, managed_keys: ManagedKeys) -> Self {
InitialisationDetails {
gateway_details,
managed_keys,
}
}
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
if !loaded_details.verify(&loaded_keys.must_get_gateway_shared_key()) {
return Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: loaded_details.details.gateway_id,
});
}
Ok(InitialisationDetails {
gateway_details: loaded_details.into(),
managed_keys: loaded_keys,
})
}
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
let client_recipient = Recipient::new(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&self.gateway_details.gateway_id)?,
);
Ok(client_recipient)
}
}
pub enum GatewaySetup {
/// The gateway specification MUST BE loaded from the underlying storage.
MustLoad,
/// Specifies usage of a new, random, gateway.
New {
/// Should the new gateway be selected based on latency.
by_latency: bool,
},
Specified {
/// Identity key of the gateway we want to try to use.
gateway_identity: IdentityKey,
},
Predefined {
/// Full gateway configuration
details: PersistedGatewayDetails,
},
ReuseConnection {
/// The authenticated ephemeral client that was created during `init`
authenticated_ephemeral_client: GatewayClient<InitOnly>,
/// Details of this pre-initialised client
details: InitialisationDetails,
},
}
impl From<PersistedGatewayDetails> for GatewaySetup {
fn from(details: PersistedGatewayDetails) -> Self {
GatewaySetup::Predefined { details }
}
}
impl From<IdentityKey> for GatewaySetup {
fn from(gateway_identity: IdentityKey) -> Self {
GatewaySetup::Specified { gateway_identity }
}
}
impl Default for GatewaySetup {
fn default() -> Self {
GatewaySetup::New { by_latency: false }
}
}
impl GatewaySetup {
pub fn new_fresh(
gateway_identity: Option<String>,
latency_based_selection: Option<bool>,
) -> Self {
if let Some(gateway_identity) = gateway_identity {
GatewaySetup::Specified { gateway_identity }
} else {
GatewaySetup::New {
by_latency: latency_based_selection.unwrap_or_default(),
}
}
}
pub fn is_must_load(&self) -> bool {
matches!(self, GatewaySetup::MustLoad)
}
pub fn has_full_details(&self) -> bool {
matches!(self, GatewaySetup::Predefined { .. }) || self.is_must_load()
}
pub async fn choose_gateway(
&self,
gateways: &[gateway::Node],
) -> Result<GatewayEndpointConfig, ClientCoreError> {
match self {
GatewaySetup::New { by_latency } => {
let mut rng = OsRng;
if *by_latency {
choose_gateway_by_latency(&mut rng, gateways).await
} else {
uniformly_random_gateway(&mut rng, gateways)
}
}
.map(Into::into),
GatewaySetup::Specified { gateway_identity } => {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
}
.map(Into::into),
_ => Err(ClientCoreError::UnexpectedGatewayDetails),
}
}
pub async fn try_get_new_gateway_details(
&self,
validator_servers: &[Url],
) -> Result<GatewayEndpointConfig, ClientCoreError> {
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?;
self.choose_gateway(&gateways).await
}
}
/// Struct describing the results of the client initialization procedure.
#[derive(Debug, Serialize)]
pub struct InitResults {
version: String,
id: String,
identity_key: String,
encryption_key: String,
gateway_id: String,
gateway_listener: String,
}
impl InitResults {
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
version: config.client.version.clone(),
id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: gateway.gateway_id.clone(),
gateway_listener: gateway.gateway_listener.clone(),
}
}
}
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Version: {}", self.version)?;
writeln!(f, "ID: {}", self.id)?;
writeln!(f, "Identity key: {}", self.identity_key)?;
writeln!(f, "Encryption: {}", self.encryption_key)?;
writeln!(f, "Gateway ID: {}", self.gateway_id)?;
write!(f, "Gateway: {}", self.gateway_listener)
}
}
// helpers for error wrapping
async fn _store_gateway_details<T, D>(
async fn _store_gateway_details<D>(
details_store: &D,
details: &PersistedGatewayDetails<T>,
details: &PersistedGatewayDetails,
) -> Result<(), ClientCoreError>
where
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
D::StorageError: Send + Sync + 'static,
T: Serialize + Send + Sync,
{
details_store
.store_gateway_details(details)
@@ -45,13 +257,12 @@ where
})
}
async fn _load_gateway_details<T, D>(
async fn _load_gateway_details<D>(
details_store: &D,
) -> Result<PersistedGatewayDetails<T>, ClientCoreError>
) -> Result<PersistedGatewayDetails, ClientCoreError>
where
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
details_store
.load_gateway_details()
@@ -73,168 +284,190 @@ where
})
}
fn ensure_valid_details<T>(
details: &PersistedGatewayDetails<T>,
fn ensure_valid_details(
details: &PersistedGatewayDetails,
loaded_keys: &ManagedKeys,
) -> Result<(), ClientCoreError> {
details.validate(loaded_keys.gateway_shared_key().as_deref())
if !details.verify(&loaded_keys.must_get_gateway_shared_key()) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
async fn setup_new_gateway<T, K, D>(
pub async fn setup_gateway_from<K, D>(
setup: GatewaySetup,
key_store: &K,
details_store: &D,
overwrite_data: bool,
selection_specification: GatewaySelectionSpecification<T>,
available_gateways: Vec<gateway::Node>,
) -> Result<InitialisationResult<T>, ClientCoreError>
gateways: Option<&[gateway::Node]>,
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
// if we're setting up new gateway, failing to load existing information is fine.
// as a matter of fact, it's only potentially a problem if we DO succeed
if _load_gateway_details(details_store).await.is_ok() && !overwrite_data {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
if _load_managed_keys(key_store).await.is_ok() && !overwrite_data {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
// I don't like how we can't deal with this variant in the match below, but we need to take ownership of internal values.
if let GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
details,
} = setup
{
// if we have already performed the full setup, forward the details.
// it's up to the caller to ensure persistence
return Ok(InitialisationResult {
details,
authenticated_ephemeral_client: Some(authenticated_ephemeral_client),
});
}
let mut rng = OsRng;
let mut new_keys = ManagedKeys::generate_new(&mut rng);
let gateway_details = match selection_specification {
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
// try load gateway details
let loaded_details = _load_gateway_details(details_store).await;
// try load keys and decide what to do based on the GatewaySetup
let mut managed_keys = match ManagedKeys::try_load(key_store).await {
Ok(loaded_keys) => {
match &setup {
GatewaySetup::MustLoad => {
// get EVERYTHING from the storage
let details = loaded_details?;
ensure_valid_details(&details, &loaded_keys)?;
// no need to persist anything as we got everything from the storage
return Ok(InitialisationDetails::new(details.into(), loaded_keys).into());
}
GatewaySetup::Predefined { details } => {
// we already have defined gateway details AND a shared key
ensure_valid_details(details, &loaded_keys)?;
// if nothing was stored or we're allowed to overwrite what's there, just persist the passed data
if overwrite_data || loaded_details.is_err() {
_store_gateway_details(details_store, details).await?;
}
return Ok(
InitialisationDetails::new(details.clone().into(), loaded_keys).into(),
);
}
GatewaySetup::Specified { gateway_identity } => {
// if that data was already stored...
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
if &existing_gateway.details.gateway_id != gateway_identity
&& !overwrite_data
{
// if our loaded details don't match requested value and we CANT overwrite it...
return Err(ClientCoreError::UnexpectedGatewayDetails);
} else if &existing_gateway.details.gateway_id == gateway_identity {
// if they do match up, just return it
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
)
.into());
}
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
GatewaySetup::New { .. } => {
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
)
.into());
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
GatewaySetup::ReuseConnection { .. } => {
unreachable!("the reuse connection variant was already manually covered")
}
}
}
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
Err(_) => {
// if we failed to load the keys, ensure we didn't provide gateway details in some form
// (in that case we CAN'T generate new keys
if setup.has_full_details() {
return Err(ClientCoreError::UnavailableSharedKey);
}
ManagedKeys::generate_new(&mut rng)
}
GatewaySelectionSpecification::Specified {
must_use_tls,
identity,
} => {
let gateway = get_specified_gateway(&identity, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
}
GatewaySelectionSpecification::Custom {
gateway_identity,
additional_data,
} => GatewayDetails::Custom(CustomGatewayDetails::new(gateway_identity, additional_data)),
};
let registration_result = if let GatewayDetails::Configured(gateway_cfg) = &gateway_details {
// if we're using a 'normal' gateway setup, do register
let our_identity = new_keys.identity_keypair();
Some(helpers::register_with_gateway(gateway_cfg, our_identity).await?)
} else {
None
};
// choose gateway
let gateway_details = setup.choose_gateway(gateways.unwrap_or_default()).await?;
let maybe_shared_keys = registration_result
.as_ref()
.map(|r| Arc::clone(&r.shared_keys));
// get our identity key
let our_identity = managed_keys.identity_keypair();
let persisted_details =
PersistedGatewayDetails::new(gateway_details, maybe_shared_keys.as_deref())?;
// Establish connection, authenticate and generate keys for talking with the gateway
let registration_result =
helpers::register_with_gateway(&gateway_details, our_identity).await?;
let shared_keys = registration_result.shared_keys;
// persist the keys
new_keys
.deal_with_gateway_key(maybe_shared_keys, key_store)
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
// persist gateway keys
managed_keys
.deal_with_gateway_key(shared_keys, key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
// persist gateway configs
// persist gateway config
_store_gateway_details(details_store, &persisted_details).await?;
Ok(InitialisationResult {
gateway_details: persisted_details.into(),
managed_keys: new_keys,
authenticated_ephemeral_client: registration_result
.map(|r| r.authenticated_ephemeral_client),
details: InitialisationDetails::new(persisted_details.into(), managed_keys),
authenticated_ephemeral_client: registration_result.authenticated_ephemeral_client,
})
}
async fn use_loaded_gateway_details<T, K, D>(
pub async fn setup_gateway<K, D>(
setup: GatewaySetup,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
overwrite_data: bool,
validator_servers: Option<&[Url]>,
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers.unwrap_or_default()).await?;
ensure_valid_details(&loaded_details, &loaded_keys)?;
// no need to persist anything as we got everything from the storage
Ok(InitialisationResult::new_loaded(
loaded_details.into(),
loaded_keys,
))
}
fn reuse_gateway_connection<T>(
authenticated_ephemeral_client: InitGatewayClient,
gateway_details: GatewayDetails<T>,
managed_keys: ManagedKeys,
) -> InitialisationResult<T> {
InitialisationResult {
gateway_details,
managed_keys,
authenticated_ephemeral_client: Some(authenticated_ephemeral_client),
}
}
pub async fn setup_gateway<T, K, D>(
setup: GatewaySetup<T>,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
match setup {
GatewaySetup::MustLoad => use_loaded_gateway_details(key_store, details_store).await,
GatewaySetup::New {
specification,
available_gateways,
overwrite_data,
} => {
setup_new_gateway(
key_store,
details_store,
overwrite_data,
specification,
available_gateways,
)
.await
}
GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details,
managed_keys,
} => Ok(reuse_gateway_connection(
authenticated_ephemeral_client,
gateway_details,
managed_keys,
)),
}
setup_gateway_from(
setup,
key_store,
details_store,
overwrite_data,
Some(&gateways),
)
.await
}
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
-329
View File
@@ -1,329 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedCustomGatewayDetails, PersistedGatewayDetails,
};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys;
use crate::config::{Config, GatewayEndpointConfig};
use crate::error::ClientCoreError;
use crate::init::{_load_gateway_details, _load_managed_keys, setup_gateway};
use nym_gateway_client::client::InitGatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::sync::Arc;
/// Result of registering with a gateway:
/// - shared keys derived between ourselves and the node
/// - an authenticated handle of an ephemeral handle created for the purposes of registration
pub struct RegistrationResult {
pub shared_keys: Arc<SharedKeys>,
pub authenticated_ephemeral_client: InitGatewayClient,
}
/// Result of fully initialising a client:
/// - details of the associated gateway
/// - all loaded (or derived) keys
/// - an optional authenticated handle of an ephemeral gateway handle created for the purposes of registration,
/// if this was the first time this client registered
pub struct InitialisationResult<T = EmptyCustomDetails> {
pub gateway_details: GatewayDetails<T>,
pub managed_keys: ManagedKeys,
pub authenticated_ephemeral_client: Option<InitGatewayClient>,
}
impl<T> InitialisationResult<T> {
pub fn new_loaded(gateway_details: GatewayDetails<T>, managed_keys: ManagedKeys) -> Self {
InitialisationResult {
gateway_details,
managed_keys,
authenticated_ephemeral_client: None,
}
}
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
match &loaded_details {
PersistedGatewayDetails::Default(loaded_default) => {
if !loaded_default.verify(&loaded_keys.must_get_gateway_shared_key()) {
return Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: loaded_default.details.gateway_id.clone(),
});
}
}
PersistedGatewayDetails::Custom(_) => {}
}
Ok(InitialisationResult {
gateway_details: loaded_details.into(),
managed_keys: loaded_keys,
authenticated_ephemeral_client: None,
})
}
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
let client_recipient = Recipient::new(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(self.gateway_details.gateway_id())?,
);
Ok(client_recipient)
}
}
/// Details of particular gateway client got registered with
#[derive(Debug, Clone)]
pub enum GatewayDetails<T = EmptyCustomDetails> {
/// Standard details of a remote gateway
Configured(GatewayEndpointConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(CustomGatewayDetails<T>),
}
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct EmptyCustomDetails {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomGatewayDetails<T = EmptyCustomDetails> {
// whatever custom method is used, gateway's identity must be known
pub gateway_id: String,
#[serde(flatten)]
pub additional_data: T,
}
impl<T> CustomGatewayDetails<T> {
pub fn new(gateway_id: String, additional_data: T) -> Self {
Self {
gateway_id,
additional_data,
}
}
}
impl<T> GatewayDetails<T> {
pub fn try_get_configured_endpoint(&self) -> Option<&GatewayEndpointConfig> {
if let GatewayDetails::Configured(endpoint) = &self {
Some(endpoint)
} else {
None
}
}
pub fn is_custom(&self) -> bool {
matches!(self, GatewayDetails::Custom(_))
}
pub fn gateway_id(&self) -> &str {
match self {
GatewayDetails::Configured(cfg) => &cfg.gateway_id,
GatewayDetails::Custom(custom) => &custom.gateway_id,
}
}
}
impl From<GatewayEndpointConfig> for GatewayDetails {
fn from(value: GatewayEndpointConfig) -> Self {
GatewayDetails::Configured(value)
}
}
impl<T> From<PersistedCustomGatewayDetails<T>> for CustomGatewayDetails<T> {
fn from(value: PersistedCustomGatewayDetails<T>) -> Self {
CustomGatewayDetails {
gateway_id: value.gateway_id,
additional_data: value.additional_data,
}
}
}
impl<T> From<CustomGatewayDetails<T>> for PersistedCustomGatewayDetails<T> {
fn from(value: CustomGatewayDetails<T>) -> Self {
PersistedCustomGatewayDetails {
gateway_id: value.gateway_id,
additional_data: value.additional_data,
}
}
}
impl<T> From<PersistedGatewayDetails<T>> for GatewayDetails<T> {
fn from(value: PersistedGatewayDetails<T>) -> Self {
match value {
PersistedGatewayDetails::Default(default) => {
GatewayDetails::Configured(default.details)
}
PersistedGatewayDetails::Custom(custom) => GatewayDetails::Custom(custom.into()),
}
}
}
#[derive(Clone)]
pub enum GatewaySelectionSpecification<T = EmptyCustomDetails> {
/// Uniformly choose a random remote gateway.
UniformRemote { must_use_tls: bool },
/// Should the new, remote, gateway be selected based on latency.
RemoteByLatency { must_use_tls: bool },
/// Gateway with this specific identity should be chosen.
// JS: I don't really like the name of this enum variant but couldn't think of anything better at the time
Specified {
must_use_tls: bool,
identity: IdentityKey,
},
// TODO: this doesn't really fit in here..., but where else to put it?
/// This client has handled the selection by itself
Custom {
gateway_identity: String,
additional_data: T,
},
}
impl<T> Default for GatewaySelectionSpecification<T> {
fn default() -> Self {
GatewaySelectionSpecification::UniformRemote {
must_use_tls: false,
}
}
}
impl<T> GatewaySelectionSpecification<T> {
pub fn new(
gateway_identity: Option<String>,
latency_based_selection: Option<bool>,
must_use_tls: bool,
) -> Self {
if let Some(identity) = gateway_identity {
GatewaySelectionSpecification::Specified {
identity,
must_use_tls,
}
} else if let Some(true) = latency_based_selection {
GatewaySelectionSpecification::RemoteByLatency { must_use_tls }
} else {
GatewaySelectionSpecification::UniformRemote { must_use_tls }
}
}
}
pub enum GatewaySetup<T = EmptyCustomDetails> {
/// The gateway specification (details + keys) MUST BE loaded from the underlying storage.
MustLoad,
/// Specifies usage of a new gateway
New {
specification: GatewaySelectionSpecification<T>,
// TODO: seems to be a bit inefficient to pass them by value
available_gateways: Vec<gateway::Node>,
/// Specifies whether old data should be overwritten whilst setting up new gateway client.
overwrite_data: bool,
},
ReuseConnection {
/// The authenticated ephemeral client that was created during `init`
authenticated_ephemeral_client: InitGatewayClient,
// Details of this pre-initialised client (i.e. gateway and keys)
gateway_details: GatewayDetails<T>,
managed_keys: ManagedKeys,
},
}
impl<T> GatewaySetup<T> {
pub fn try_reuse_connection(
init_res: InitialisationResult<T>,
) -> Result<Self, ClientCoreError> {
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
Ok(GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details: init_res.gateway_details,
managed_keys: init_res.managed_keys,
})
} else {
Err(ClientCoreError::NoInitClientPresent)
}
}
pub async fn try_setup<K, D>(
self,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
setup_gateway(self, key_store, details_store).await
}
pub fn is_must_load(&self) -> bool {
matches!(self, GatewaySetup::MustLoad)
}
pub fn has_full_details(&self) -> bool {
self.is_must_load()
}
}
/// Struct describing the results of the client initialization procedure.
#[derive(Debug, Serialize)]
pub struct InitResults {
version: String,
id: String,
identity_key: String,
encryption_key: String,
gateway_id: String,
gateway_listener: String,
}
impl InitResults {
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
version: config.client.version.clone(),
id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: gateway.gateway_id.clone(),
gateway_listener: gateway.gateway_listener.clone(),
}
}
}
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Version: {}", self.version)?;
writeln!(f, "ID: {}", self.id)?;
writeln!(f, "Identity key: {}", self.identity_key)?;
writeln!(f, "Encryption: {}", self.encryption_key)?;
writeln!(f, "Gateway ID: {}", self.gateway_id)?;
write!(f, "Gateway: {}", self.gateway_listener)
}
}
-5
View File
@@ -5,11 +5,6 @@ pub mod config;
pub mod error;
pub mod init;
pub use nym_topology::{
HardcodedTopologyProvider, NymTopology, NymTopologyError, SerializableNymTopology,
SerializableTopologyError, TopologyProvider,
};
#[cfg(target_arch = "wasm32")]
pub(crate) fn spawn_future<F>(future: F)
where
+33 -52
View File
@@ -7,7 +7,6 @@ pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
};
use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt};
use log::*;
@@ -40,23 +39,9 @@ use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep;
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
pub struct GatewayConfig {
pub gateway_identity: identity::PublicKey,
// currently a dead field
pub gateway_owner: Option<String>,
pub gateway_listener: String,
}
// TODO: this should be refactored into a state machine that keeps track of its authentication state
pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
authenticated: bool,
disabled_credentials_mode: bool,
@@ -84,12 +69,17 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
}
impl<C, St> GatewayClient<C, St> {
// TODO: put it all in a Config struct
#[allow(clippy::too_many_arguments)]
pub fn new(
config: GatewayConfig,
gateway_address: String,
local_identity: Arc<identity::KeyPair>,
gateway_identity: identity::PublicKey,
// TODO: make it mandatory. if you don't want to pass it, use `new_init`
shared_key: Option<Arc<SharedKeys>>,
packet_router: PacketRouter,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, St>>,
shutdown: TaskClient,
) -> Self {
@@ -97,13 +87,13 @@ impl<C, St> GatewayClient<C, St> {
authenticated: false,
disabled_credentials_mode: true,
bandwidth_remaining: 0,
gateway_address: config.gateway_listener,
gateway_identity: config.gateway_identity,
gateway_address,
gateway_identity,
local_identity,
shared_key,
connection: SocketState::NotConnected,
packet_router,
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
response_timeout_duration,
bandwidth_controller,
should_reconnect_on_failure: true,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
@@ -112,34 +102,21 @@ impl<C, St> GatewayClient<C, St> {
}
}
#[must_use]
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) {
self.disabled_credentials_mode = disabled_credentials_mode;
self
}
#[must_use]
pub fn with_reconnection_on_failure(mut self, should_reconnect_on_failure: bool) -> Self {
self.should_reconnect_on_failure = should_reconnect_on_failure;
self
// TODO: later convert into proper builder methods
pub fn with_reconnection_on_failure(&mut self, should_reconnect_on_failure: bool) {
self.should_reconnect_on_failure = should_reconnect_on_failure
}
#[must_use]
pub fn with_response_timeout(mut self, response_timeout_duration: Duration) -> Self {
self.response_timeout_duration = response_timeout_duration;
self
pub fn with_reconnection_attempts(&mut self, reconnection_attempts: usize) {
self.reconnection_attempts = reconnection_attempts
}
#[must_use]
pub fn with_reconnection_attempts(mut self, reconnection_attempts: usize) -> Self {
self.reconnection_attempts = reconnection_attempts;
self
}
#[must_use]
pub fn with_reconnection_backoff(mut self, backoff: Duration) -> Self {
self.reconnection_backoff = backoff;
self
pub fn with_reconnection_backoff(&mut self, backoff: Duration) {
self.reconnection_backoff = backoff
}
pub fn gateway_identity(&self) -> identity::PublicKey {
@@ -784,14 +761,16 @@ impl<C, St> GatewayClient<C, St> {
}
}
// type alias for an ease of use
pub type InitGatewayClient = GatewayClient<InitOnly>;
pub struct InitOnly;
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
// for initialisation we do not need credential storage. Though it's still a bit weird we have to set the generic...
pub fn new_init(config: GatewayConfig, local_identity: Arc<identity::KeyPair>) -> Self {
pub fn new_init(
gateway_address: String,
gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>,
response_timeout_duration: Duration,
) -> Self {
use futures::channel::mpsc;
// note: this packet_router is completely invalid in normal circumstances, but "works"
@@ -805,13 +784,13 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
authenticated: false,
disabled_credentials_mode: true,
bandwidth_remaining: 0,
gateway_address: config.gateway_listener,
gateway_identity: config.gateway_identity,
gateway_address,
gateway_identity,
local_identity,
shared_key: None,
connection: SocketState::NotConnected,
packet_router,
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
response_timeout_duration,
bandwidth_controller: None,
should_reconnect_on_failure: false,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
@@ -822,7 +801,9 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
pub fn upgrade<C, St>(
self,
packet_router: PacketRouter,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, St>>,
shutdown: TaskClient,
) -> GatewayClient<C, St> {
@@ -841,8 +822,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
local_identity: self.local_identity,
shared_key: self.shared_key,
connection: self.connection,
packet_router,
response_timeout_duration: self.response_timeout_duration,
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
response_timeout_duration,
bandwidth_controller,
should_reconnect_on_failure: self.should_reconnect_on_failure,
reconnection_attempts: self.reconnection_attempts,
+6 -16
View File
@@ -13,17 +13,17 @@ pub enum GatewayClientError {
#[error("Connection to the gateway is not established")]
ConnectionNotEstablished,
#[error("Gateway returned an error response: {0}")]
#[error("Gateway returned an error response - {0}")]
GatewayError(String),
#[error("There was a network error: {0}")]
#[error("There was a network error - {0}")]
NetworkError(#[from] WsError),
#[cfg(target_arch = "wasm32")]
#[error("There was a network error: {0}")]
#[error("There was a network error")]
NetworkErrorWasm(#[from] JsError),
#[error("Invalid URL: {0}")]
#[error("Invalid URL - {0}")]
InvalidURL(String),
#[error("No shared key was provided or obtained")]
@@ -32,7 +32,7 @@ pub enum GatewayClientError {
#[error("No bandwidth controller provided")]
NoBandwidthControllerAvailable,
#[error("Bandwidth controller error: {0}")]
#[error("Bandwidth controller error - {0}")]
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
#[error("Connection was abruptly closed")]
@@ -62,7 +62,7 @@ pub enum GatewayClientError {
#[error("Connection is in an invalid state - please send a bug report")]
ConnectionInInvalidState,
#[error("Failed to finish registration handshake: {0}")]
#[error("Failed to finish registration handshake - {0}")]
RegistrationFailure(HandshakeError),
#[error("Authentication failure")]
@@ -76,16 +76,6 @@ pub enum GatewayClientError {
#[error("Attempted to negotiate connection with gateway using incompatible protocol version. Ours is {current} and the gateway reports {gateway:?}")]
IncompatibleProtocol { gateway: Option<u8>, current: u8 },
#[error(
"The packet router hasn't been set - are you sure you started up the client correctly?"
)]
PacketRouterUnavailable,
#[error(
"this operation couldn't be completed as the program is in the process of shutting down"
)]
ShutdownInProgress,
}
impl GatewayClientError {
+4 -7
View File
@@ -2,23 +2,20 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::GatewayClientError;
pub use client::GatewayClient;
use log::warn;
use nym_gateway_requests::BinaryResponse;
use tungstenite::{protocol::Message, Error as WsError};
pub use client::{GatewayClient, GatewayConfig};
pub use nym_gateway_requests::registration::handshake::SharedKeys;
pub use packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
PacketRouter,
};
pub use traits::GatewayPacketRouter;
use tungstenite::{protocol::Message, Error as WsError};
pub use nym_gateway_requests::registration::handshake::SharedKeys;
pub mod client;
pub mod error;
pub mod packet_router;
pub mod socket_state;
pub mod traits;
/// Helper method for reading from websocket stream. Helps to flatten the structure.
pub(crate) fn cleanup_socket_message(
@@ -5,8 +5,10 @@
// I will gladly take any suggestions on how to rename this.
use crate::error::GatewayClientError;
use crate::GatewayPacketRouter;
use futures::channel::mpsc;
use log::*;
use nym_sphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx::params::packet_sizes::PacketSize;
use nym_task::TaskClient;
pub type MixnetMessageSender = mpsc::UnboundedSender<Vec<Vec<u8>>>;
@@ -35,52 +37,77 @@ impl PacketRouter {
}
}
pub fn route_mixnet_messages(
&self,
received_messages: Vec<Vec<u8>>,
pub fn route_received(
&mut self,
unwrapped_packets: Vec<Vec<u8>>,
) -> Result<(), GatewayClientError> {
if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) {
// check if the failure is due to the shutdown being in progress and thus the receiver channel
// having already been dropped
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send mixnet messages due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress);
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send mixnet message: {err}");
}
Ok(())
}
let mut received_messages = Vec::new();
let mut received_acks = Vec::new();
pub fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), GatewayClientError> {
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
// check if the failure is due to the shutdown being in progress and thus the receiver channel
// having already been dropped
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send acks due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress);
// remember: gateway removes final layer of sphinx encryption and from the unwrapped
// data he takes the SURB-ACK and first hop address.
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let outfox_ack_overhead =
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
for received_packet in unwrapped_packets {
if received_packet.len() == PacketSize::AckPacket.plaintext_size()
// we don't know the real size of the payload, it could be anything <= 48 bytes
|| received_packet.len() <= PacketSize::OutfoxAckPacket.plaintext_size()
{
received_acks.push(received_packet);
} else if received_packet.len()
== PacketSize::RegularPacket.plaintext_size() - ack_overhead
|| received_packet.len()
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead
|| received_packet.len()
== PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead
{
trace!("routing regular packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket8.plaintext_size() - ack_overhead
{
trace!("routing extended8 packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket16.plaintext_size() - ack_overhead
{
trace!("routing extended16 packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket32.plaintext_size() - ack_overhead
{
trace!("routing extended32 packet");
received_messages.push(received_packet);
} else {
// this can happen if other clients are not padding their messages
warn!("Received message of unexpected size. Probably from an outdated client... len: {}", received_packet.len());
received_messages.push(received_packet);
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send acks: {err}");
}
if !received_messages.is_empty() {
trace!("routing 'real'");
if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) {
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send mixnet message due to receiver task shutdown");
return Err(GatewayClientError::MixnetMsgSenderFailedToSend);
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send mixnet message: {err}");
}
}
if !received_acks.is_empty() {
trace!("routing acks");
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
error!("failed to send ack: {err}");
};
}
Ok(())
}
}
impl GatewayPacketRouter for PacketRouter {
type Error = GatewayClientError;
// note: this trait tries to decide whether a given message is an ack or a data message
fn route_mixnet_messages(&self, received_messages: Vec<Vec<u8>>) -> Result<(), Self::Error> {
self.route_mixnet_messages(received_messages)
}
fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), Self::Error> {
self.route_acks(received_acks)
}
}
@@ -3,7 +3,6 @@
use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter;
use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_messages, try_decrypt_binary_message};
use futures::channel::oneshot;
use futures::stream::{SplitSink, SplitStream};
@@ -1,96 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use log::{error, trace, warn};
use nym_sphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx::params::PacketSize;
pub trait GatewayPacketRouter {
type Error: std::error::Error;
fn route_received(&self, unwrapped_packets: Vec<Vec<u8>>) -> Result<(), Self::Error> {
let mut received_messages = Vec::new();
let mut received_acks = Vec::new();
// remember: gateway removes final layer of sphinx encryption and from the unwrapped
// data he takes the SURB-ACK and first hop address.
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let outfox_ack_overhead =
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
for received_packet in unwrapped_packets {
// note: if we ever fail to route regular outfox, it might be because I've removed a match on
// `size == PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead` since it seemed
// redundant given we have `size == PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead`
// and all the headers should have already be stripped at this point
match received_packet.len() {
n if n == PacketSize::AckPacket.plaintext_size() => {
trace!("received sphinx ack");
received_acks.push(received_packet);
}
n if n <= PacketSize::OutfoxAckPacket.plaintext_size() => {
// we don't know the real size of the payload, it could be anything <= 48 bytes
trace!("received outfox ack");
received_acks.push(received_packet);
}
n if n == PacketSize::RegularPacket.plaintext_size() - sphinx_ack_overhead => {
trace!("received regular sphinx packet");
received_messages.push(received_packet);
}
n if n
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead =>
{
trace!("received regular outfox packet");
received_messages.push(received_packet);
}
n if n == PacketSize::ExtendedPacket8.plaintext_size() - sphinx_ack_overhead => {
trace!("received extended8 packet");
received_messages.push(received_packet);
}
n if n == PacketSize::ExtendedPacket16.plaintext_size() - sphinx_ack_overhead => {
trace!("received extended16 packet");
received_messages.push(received_packet);
}
n if n == PacketSize::ExtendedPacket32.plaintext_size() - sphinx_ack_overhead => {
trace!("received extended32 packet");
received_messages.push(received_packet);
}
n => {
// this can happen if other clients are not padding their messages
warn!("Received message of unexpected size. Probably from an outdated client... len: {n}");
received_messages.push(received_packet);
}
}
}
if !received_messages.is_empty() {
trace!("routing {} received packets", received_messages.len());
if let Err(err) = self.route_mixnet_messages(received_messages) {
error!("failed to route received messages: {err}");
return Err(err);
}
}
if !received_acks.is_empty() {
trace!("routing {} received acks", received_acks.len());
if let Err(err) = self.route_acks(received_acks) {
error!("failed to route received acks: {err}");
return Err(err);
}
}
Ok(())
}
fn route_mixnet_messages(&self, received_messages: Vec<Vec<u8>>) -> Result<(), Self::Error>;
fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), Self::Error>;
}
@@ -56,7 +56,7 @@ impl PacketForwarder {
log::trace!("PacketForwarder: Received shutdown");
}
Some(mix_packet) = self.packet_receiver.next() => {
trace!("Going to forward packet to {}", mix_packet.next_hop());
trace!("Going to forward packet to {:?}", mix_packet.next_hop());
let next_hop = mix_packet.next_hop();
let packet_type = mix_packet.packet_type();
+6 -5
View File
@@ -33,17 +33,18 @@ pub fn may_get_home() -> Option<PathBuf> {
}
pub trait NymConfigTemplate: Serialize {
fn template(&self) -> &'static str;
fn template() -> &'static str;
fn format_to_string(&self) -> String {
// it is responsibility of whoever is implementing the trait to ensure the template is valid
Handlebars::new()
.render_template(self.template(), &self)
.render_template(Self::template(), &self)
.unwrap()
}
fn format_to_writer<W: Write>(&self, writer: W) -> io::Result<()> {
if let Err(err) = Handlebars::new().render_template_to_write(self.template(), &self, writer)
if let Err(err) =
Handlebars::new().render_template_to_write(Self::template(), &self, writer)
{
match err {
TemplateRenderError::IOError(err, _) => return Err(err),
@@ -63,7 +64,7 @@ where
C: NymConfigTemplate,
P: AsRef<Path>,
{
log::debug!("trying to save config file to {}", path.as_ref().display());
log::trace!("trying to save config file to {}", path.as_ref().display());
let file = File::create(path.as_ref())?;
// TODO: check for whether any of our configs stores anything sensitive
@@ -107,7 +108,7 @@ where
//
//
// pub trait NymConfig: Default + Serialize + DeserializeOwned {
// fn template(&self) -> &'static str;
// fn template() -> &'static str;
//
// fn config_file_name() -> String {
// "config.toml".to_string()
+2 -2
View File
@@ -13,7 +13,7 @@ thiserror = "1.0"
tokio = { version = "1.24.1", features = ["sync"]}
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
version = "0.5"
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
@@ -22,5 +22,5 @@ features = [ "rt-multi-thread", "net", "signal", "fs" ]
[build-dependencies]
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
@@ -29,11 +29,11 @@ impl PersistentStorage {
database_path.as_ref().as_os_str()
);
let opts = sqlx::sqlite::SqliteConnectOptions::new()
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
.filename(database_path)
.create_if_missing(true);
let opts = opts.disable_statement_logging();
opts.disable_statement_logging();
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
Ok(db) => db,
+1 -1
View File
@@ -71,7 +71,7 @@ pub async fn setup_recovery_storage(recovery_dir: PathBuf) -> RecoveryStorage {
pub async fn setup_persistent_storage(client_home_directory: PathBuf) -> PersistentStorage {
let data_dir = client_home_directory.join(DEFAULT_DATA_DIR);
let paths = CommonClientPaths::new_base(data_dir);
let paths = CommonClientPaths::new_default(data_dir);
let db_path = paths.credentials_database;
nym_credential_storage::initialise_persistent_storage(db_path).await
@@ -150,13 +150,6 @@ impl PublicKey {
}
}
#[cfg(feature = "sphinx")]
impl From<PublicKey> for DestinationAddressBytes {
fn from(value: PublicKey) -> Self {
value.derive_destination_address()
}
}
impl FromStr for PublicKey {
type Err = Ed25519RecoveryError;
+9 -8
View File
@@ -4,9 +4,9 @@
use crate::var_names;
use crate::{DenomDetails, ValidatorDetails};
pub const NETWORK_NAME: &str = "mainnet";
pub(crate) const NETWORK_NAME: &str = "mainnet";
pub const BECH32_PREFIX: &str = "n";
pub(crate) const BECH32_PREFIX: &str = "n";
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
@@ -15,12 +15,13 @@ pub const MIXNET_CONTRACT_ADDRESS: &str =
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
pub const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const EPHEMERA_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const EPHEMERA_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
-1
View File
@@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { workspace = true }
dirs = "4.0"
futures = { workspace = true }
log = { workspace = true }
@@ -17,7 +17,7 @@ use std::path::PathBuf;
//
// //
// // impl NymConfig for OldConfigV1_1_13 {
// // fn template(&self) -> &'static str {
// // fn template() -> &'static str {
// // // not intended to be used
// // unimplemented!()
// // }
+7 -13
View File
@@ -19,13 +19,12 @@ use nym_client_core::client::base_client::{
use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::replies::reply_storage::ReplyStorageBackend;
use nym_client_core::config::DebugConfig;
use nym_client_core::init::types::GatewaySetup;
use nym_client_core::init::GatewaySetup;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType;
use nym_task::{TaskClient, TaskHandle};
use nym_task::{TaskClient, TaskManager};
use anyhow::anyhow;
use std::error::Error;
use std::path::PathBuf;
@@ -45,7 +44,7 @@ pub enum Socks5ControlMessage {
pub struct StartedSocks5Client {
/// Handle for managing graceful shutdown of this client. If dropped, the client will be stopped.
pub shutdown_handle: TaskHandle,
pub shutdown_handle: TaskManager,
/// Address of the started client
pub address: Recipient,
@@ -156,7 +155,7 @@ where
pub async fn run_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
let started = self.start().await?;
let res = started.shutdown_handle.wait_for_shutdown().await;
let res = started.shutdown_handle.catch_interrupt().await;
log::info!("Stopping nym-socks5-client");
res
}
@@ -169,12 +168,7 @@ where
) -> Result<(), Box<dyn Error + Send + Sync>> {
// Start the main task
let started = self.start().await?;
let mut shutdown = started
.shutdown_handle
.try_into_task_manager()
.ok_or(anyhow!(
"attempted to use `run_and_listen` without owning shutdown handle"
))?;
let mut shutdown = started.shutdown_handle;
// Listen to status messages from task, that we forward back to the caller
shutdown.start_status_listener(sender).await;
@@ -245,7 +239,7 @@ where
client_output,
client_state,
self_address,
started_client.task_handle.get_handle(),
started_client.task_manager.subscribe(),
packet_type,
);
@@ -253,7 +247,7 @@ where
info!("The address of this client is: {self_address}");
Ok(StartedSocks5Client {
shutdown_handle: started_client.task_handle,
shutdown_handle: started_client.task_manager,
address: self_address,
})
}
@@ -507,8 +507,8 @@ impl SocksClient {
);
}
SocksCommand::Bind => return Err(SocksProxyError::BindNotSupported), // not handled
SocksCommand::UdpAssociate => return Err(SocksProxyError::UdpNotSupported),
SocksCommand::Bind => unimplemented!(), // not handled
SocksCommand::UdpAssociate => unimplemented!(), // not handled
};
Ok(())
@@ -83,12 +83,6 @@ pub enum SocksProxyError {
#[from]
source: Socks5RequestError,
},
#[error("SOCKS5 UDP not (yet) supported")]
UdpNotSupported,
#[error("SOCKS5 BIND not (yet) supported")]
BindNotSupported,
}
/// DST.addr variant types
@@ -132,7 +132,7 @@ impl Controller {
} else {
// check if there were any pending messages
if let Some(pending) = self.pending_messages.remove(&conn_id) {
debug!("There were some pending messages for {conn_id}");
debug!("There were some pending messages for {}", conn_id);
for data in pending {
self.send_to_connection(data)
}
@@ -141,9 +141,12 @@ impl Controller {
}
fn remove_connection(&mut self, conn_id: ConnectionId) {
debug!("Removing {conn_id} from controller");
debug!("Removing {} from controller", conn_id);
if self.active_connections.remove(&conn_id).is_none() {
error!("tried to remove non-existing connection with id: {conn_id}",)
error!(
"tried to remove non-existing connection with id: {:?}",
conn_id
)
}
self.recently_closed.insert(conn_id);
+1 -1
View File
@@ -14,6 +14,6 @@ log = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "chrono"]}
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]}
thiserror = "1"
tokio = { version = "1.24.1", features = [ "time" ] }
+4 -8
View File
@@ -14,18 +14,14 @@ thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "sync"] }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
workspace = true
version = "1.24.1"
features = ["signal", "time", "rt"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
workspace = true
version = "0.4"
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
workspace = true
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
features = ["tokio"]
version = "0.2.83"
[dev-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] }
+1 -1
View File
@@ -7,7 +7,7 @@ pub mod manager;
pub mod signal;
pub mod spawn;
pub use manager::{StatusReceiver, StatusSender, TaskClient, TaskHandle, TaskManager};
pub use manager::{StatusReceiver, StatusSender, TaskClient, TaskManager};
#[cfg(not(target_arch = "wasm32"))]
pub use signal::wait_for_signal_and_error;
+33 -145
View File
@@ -1,22 +1,19 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::future::Future;
use std::{error::Error, time::Duration};
use futures::{future::pending, FutureExt, SinkExt, StreamExt};
use log::{log, Level};
use std::future::Future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{error::Error, time::Duration};
use tokio::sync::{
mpsc,
watch::{self, error::SendError},
use tokio::{
sync::{
mpsc,
watch::{self, error::SendError},
},
time::sleep,
};
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::{sleep, timeout};
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::{sleep, timeout};
const DEFAULT_SHUTDOWN_TIMER_SECS: u64 = 5;
pub(crate) type SentError = Box<dyn Error + Send + Sync>;
@@ -146,17 +143,6 @@ impl TaskManager {
}
}
pub fn subscribe_named<S: Into<String>>(&self, suffix: S) -> TaskClient {
let task_client = self.subscribe();
let suffix = suffix.into();
let child_name = if let Some(base) = &self.name {
format!("{base}-{suffix}")
} else {
format!("unknown-{suffix}")
};
task_client.named(child_name)
}
pub fn signal_shutdown(&self) -> Result<(), SendError<()>> {
self.notify_tx.send(())
}
@@ -173,9 +159,9 @@ impl TaskManager {
crate::spawn::spawn(async move {
loop {
if let Some(msg) = task_status_rx.next().await {
log::trace!("Got msg: {msg}");
log::trace!("Got msg: {}", msg);
if let Err(msg) = sender.send(msg).await {
log::error!("Error sending status message: {msg}");
log::error!("Error sending status message: {}", msg);
}
} else {
log::trace!("Stopping since channel closed");
@@ -219,10 +205,16 @@ impl TaskManager {
#[cfg(not(target_arch = "wasm32"))]
let interrupt_future = tokio::signal::ctrl_c();
// in wasm we'll never get our shutdown anyway...
#[cfg(target_arch = "wasm32")]
let interrupt_future = futures::future::pending::<()>();
let wait_future = sleep(Duration::from_secs(self.shutdown_timer_secs));
#[cfg(not(target_arch = "wasm32"))]
let wait_future = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs));
// TODO: we should be using a `Delay` here for wasm
#[cfg(target_arch = "wasm32")]
let wait_future = futures::future::pending::<()>();
tokio::select! {
_ = self.notify_tx.closed() => {
@@ -246,10 +238,7 @@ pub struct TaskClient {
name: Option<String>,
// If a shutdown notification has been registered
// the reason for having an atomic here is to be able to cheat and modify that value whilst
// holding an immutable reference to the `TaskClient`.
// note: using `Relaxed` ordering everywhere is fine since it's not shared between threads
shutdown: AtomicBool,
shutdown: bool,
// Listen for shutdown notifications, as well as a mechanism to report back that we have
// finished (the receiver is closed).
@@ -283,7 +272,7 @@ impl Clone for TaskClient {
TaskClient {
name,
shutdown: AtomicBool::new(self.shutdown.load(Ordering::Relaxed)),
shutdown: self.shutdown,
notify: self.notify.clone(),
return_error: self.return_error.clone(),
drop_error: self.drop_error.clone(),
@@ -297,6 +286,7 @@ impl TaskClient {
const MAX_NAME_LENGTH: usize = 128;
const OVERFLOW_NAME: &'static str = "reached maximum TaskClient children name depth";
#[cfg(not(target_arch = "wasm32"))]
const SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT: Duration = Duration::from_secs(5);
fn new(
@@ -307,7 +297,7 @@ impl TaskClient {
) -> TaskClient {
TaskClient {
name: None,
shutdown: AtomicBool::new(false),
shutdown: false,
notify,
return_error,
drop_error,
@@ -351,17 +341,6 @@ impl TaskClient {
self
}
#[must_use]
pub fn with_suffix<S: Into<String>>(self, suffix: S) -> Self {
let suffix = suffix.into();
let name = if let Some(base) = &self.name {
format!("{base}-{suffix}")
} else {
format!("unknown-{suffix}")
};
self.named(name)
}
pub async fn run_future<Fut, T>(&mut self, fut: Fut) -> Option<T>
where
Fut: Future<Output = T>,
@@ -381,7 +360,7 @@ impl TaskClient {
let (task_status_tx, _task_status_rx) = futures::channel::mpsc::channel(128);
TaskClient {
name: None,
shutdown: AtomicBool::new(false),
shutdown: false,
notify: notify_rx,
return_error: task_halt_tx,
drop_error: task_drop_tx,
@@ -398,7 +377,7 @@ impl TaskClient {
if self.mode.is_dummy() {
false
} else {
self.shutdown.load(Ordering::Relaxed)
self.shutdown
}
}
@@ -406,11 +385,11 @@ impl TaskClient {
if self.mode.is_dummy() {
return pending().await;
}
if self.shutdown.load(Ordering::Relaxed) {
if self.shutdown {
return;
}
let _ = self.notify.changed().await;
self.shutdown.store(true, Ordering::Relaxed);
self.shutdown = true;
}
pub async fn recv_with_delay(&mut self) {
@@ -424,31 +403,29 @@ impl TaskClient {
pub async fn recv_timeout(&mut self) {
if self.mode.is_dummy() {
#[cfg_attr(target_arch = "wasm32", allow(clippy::needless_return))]
return pending().await;
}
if let Err(timeout) = timeout(
#[cfg(not(target_arch = "wasm32"))]
tokio::time::timeout(
Self::SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT,
self.recv(),
)
.await
{
self.log(Level::Error, "Task stopped without shutdown called");
panic!("{:?}: {timeout}", self.name)
}
.expect("Task stopped without shutdown called");
}
pub fn is_shutdown_poll(&self) -> bool {
pub fn is_shutdown_poll(&mut self) -> bool {
if self.mode.is_dummy() {
return false;
}
if self.shutdown.load(Ordering::Relaxed) {
if self.shutdown {
return true;
}
match self.notify.has_changed() {
Ok(has_changed) => {
if has_changed {
self.shutdown.store(true, Ordering::Relaxed);
self.shutdown = true;
}
has_changed
}
@@ -543,95 +520,6 @@ impl ClientOperatingMode {
}
}
#[derive(Debug)]
pub enum TaskHandle {
/// Full [`TaskManager`] that was created by the underlying task.
Internal(TaskManager),
/// `[TaskClient]` that was passed from an external task, that controls the shutdown process.
External(TaskClient),
}
impl From<TaskManager> for TaskHandle {
fn from(value: TaskManager) -> Self {
TaskHandle::Internal(value)
}
}
impl From<TaskClient> for TaskHandle {
fn from(value: TaskClient) -> Self {
TaskHandle::External(value)
}
}
impl Default for TaskHandle {
fn default() -> Self {
TaskHandle::Internal(TaskManager::default())
}
}
impl TaskHandle {
#[must_use]
pub fn name_if_unnamed<S: Into<String>>(self, name: S) -> Self {
match self {
TaskHandle::Internal(task_manager) => {
if task_manager.name.is_none() {
TaskHandle::Internal(task_manager.named(name))
} else {
TaskHandle::Internal(task_manager)
}
}
TaskHandle::External(task_client) => {
if task_client.name.is_none() {
TaskHandle::External(task_client.named(name))
} else {
TaskHandle::External(task_client)
}
}
}
}
#[must_use]
pub fn named<S: Into<String>>(self, name: S) -> Self {
match self {
TaskHandle::Internal(task_manager) => TaskHandle::Internal(task_manager.named(name)),
TaskHandle::External(task_client) => TaskHandle::External(task_client.named(name)),
}
}
pub fn fork<S: Into<String>>(&self, child_suffix: S) -> TaskClient {
match self {
TaskHandle::External(shutdown) => shutdown.fork(child_suffix),
TaskHandle::Internal(shutdown) => shutdown.subscribe_named(child_suffix),
}
}
pub fn get_handle(&self) -> TaskClient {
match self {
TaskHandle::External(shutdown) => shutdown.clone(),
TaskHandle::Internal(shutdown) => shutdown.subscribe(),
}
}
pub fn try_into_task_manager(self) -> Option<TaskManager> {
match self {
TaskHandle::External(_) => None,
TaskHandle::Internal(shutdown) => Some(shutdown),
}
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn wait_for_shutdown(self) -> Result<(), SentError> {
match self {
TaskHandle::Internal(task_manager) => task_manager.catch_interrupt().await,
TaskHandle::External(mut task_client) => {
task_client.recv().await;
Ok(())
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
-3
View File
@@ -21,9 +21,6 @@ pub enum NymTopologyError {
#[error("Gateway with identity key {identity_key} doesn't exist")]
NonExistentGatewayError { identity_key: String },
#[error("timed out while waiting for gateway '{identity_key}' to come online")]
TimedOutWaitingForGateway { identity_key: String },
#[error("Wanted to create a mix route with {requested} hops, while only {available} layers are available")]
InvalidNumberOfHopsError { available: usize, requested: usize },
+1 -20
View File
@@ -8,7 +8,6 @@ use nym_sphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress};
use nym_sphinx_types::Node as SphinxNode;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::fmt::Formatter;
use std::io;
use std::net::SocketAddr;
use thiserror::Error;
@@ -29,7 +28,7 @@ pub enum GatewayConversionError {
},
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct Node {
pub owner: String,
pub host: NetworkAddress,
@@ -42,20 +41,6 @@ pub struct Node {
pub version: NodeVersion,
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("gateway::Node")
.field("host", &self.host)
.field("owner", &self.owner)
.field("mix_host", &self.mix_host)
.field("clients_port", &self.clients_port)
.field("identity_key", &self.identity_key.to_base58_string())
.field("sphinx_key", &self.sphinx_key.to_base58_string())
.field("version", &self.version)
.finish()
}
}
impl Node {
pub fn parse_host(raw: &str) -> Result<NetworkAddress, GatewayConversionError> {
raw.parse()
@@ -84,10 +69,6 @@ impl Node {
pub fn clients_address(&self) -> String {
format!("ws://{}:{}", self.host, self.clients_port)
}
pub fn clients_address_tls(&self) -> String {
format!("wss://{}:443", self.host)
}
}
impl fmt::Display for Node {
+5 -18
View File
@@ -33,9 +33,7 @@ pub mod provider_trait;
pub(crate) mod serde;
#[cfg(feature = "serializable")]
pub use crate::serde::{
SerializableGateway, SerializableMixNode, SerializableNymTopology, SerializableTopologyError,
};
pub use crate::serde::{SerializableNymTopology, SerializableTopologyError};
#[cfg(feature = "provider-trait")]
pub use provider_trait::{HardcodedTopologyProvider, TopologyProvider};
@@ -121,17 +119,6 @@ impl NymTopology {
NymTopology { mixes, gateways }
}
pub fn new_unordered(unordered_mixes: Vec<mix::Node>, gateways: Vec<gateway::Node>) -> Self {
let mut mixes = BTreeMap::new();
for node in unordered_mixes.into_iter() {
let layer = node.layer as MixLayer;
let layer_entry = mixes.entry(layer).or_insert_with(Vec::new);
layer_entry.push(node)
}
NymTopology { mixes, gateways }
}
#[cfg(feature = "serializable")]
pub fn new_from_file<P: AsRef<std::path::Path>>(path: P) -> std::io::Result<Self> {
let file = std::fs::File::open(path)?;
@@ -415,8 +402,8 @@ pub fn nym_topology_from_detailed(
let layer = bond.layer as MixLayer;
if layer == 0 || layer > 3 {
warn!(
"{} says it's on invalid layer {layer}!",
bond.mix_node.identity_key
"{} says it's on invalid layer {}!",
bond.mix_node.identity_key, layer
);
continue;
}
@@ -427,7 +414,7 @@ pub fn nym_topology_from_detailed(
match bond.try_into() {
Ok(mix) => layer_entry.push(mix),
Err(err) => {
warn!("Mix {mix_id} / {mix_identity} is malformed: {err}");
warn!("Mix {} / {} is malformed - {err}", mix_id, mix_identity);
continue;
}
}
@@ -439,7 +426,7 @@ pub fn nym_topology_from_detailed(
match bond.try_into() {
Ok(gate) => gateways.push(gate),
Err(err) => {
warn!("Gateway {gate_id} is malformed: {err}");
warn!("Gateway {} is malformed - {err}", gate_id);
continue;
}
}
+1 -17
View File
@@ -8,7 +8,6 @@ use nym_mixnet_contract_common::{MixId, MixNodeBond};
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx_types::Node as SphinxNode;
use std::convert::{TryFrom, TryInto};
use std::fmt::Formatter;
use std::io;
use std::net::SocketAddr;
use thiserror::Error;
@@ -29,7 +28,7 @@ pub enum MixnodeConversionError {
},
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct Node {
pub mix_id: MixId,
pub owner: String,
@@ -43,21 +42,6 @@ pub struct Node {
pub version: NodeVersion,
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("mix::Node")
.field("mix_id", &self.mix_id)
.field("owner", &self.owner)
.field("host", &self.host)
.field("mix_host", &self.mix_host)
.field("identity_key", &self.identity_key.to_base58_string())
.field("sphinx_key", &self.sphinx_key.to_base58_string())
.field("layer", &self.layer)
.field("version", &self.version)
.finish()
}
}
impl Node {
pub fn parse_host(raw: &str) -> Result<NetworkAddress, MixnodeConversionError> {
raw.parse()

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