Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9fb3f6988 | |||
| 56a384ea09 | |||
| c7da8a7594 | |||
| da404b636d | |||
| d8821c26bc | |||
| a06c2fb70c | |||
| 908191914f | |||
| b05ce4c733 | |||
| 382464a143 | |||
| 05f3af765b | |||
| a9d3e5047d | |||
| c424f7d3f7 | |||
| 093bb18119 | |||
| 4d5bd0ddf4 | |||
| 48d7e934b7 | |||
| ff7c359d0e | |||
| d0b91444a0 | |||
| 8c39cdf434 | |||
| fef164bf93 | |||
| 759cb4b9b4 | |||
| a800cbae02 | |||
| e4a2c639fc | |||
| a67a9c4b32 | |||
| a450e2910f | |||
| bd75c9c78d | |||
| fbfec25228 | |||
| 81db915401 | |||
| b27c7d0b14 | |||
| ed3a58b6a2 | |||
| 93733e73a2 | |||
| d7f69433d6 | |||
| ee282cfe9d | |||
| 1c13d466b1 | |||
| 238dd533a8 | |||
| 2d925c24c7 | |||
| e6d5c5ec8c | |||
| b4a7b9ed75 | |||
| 7bed01902e | |||
| a77980a0da | |||
| d328bc15f8 | |||
| 871d88e3ed | |||
| 3904afa747 | |||
| 2f8442760f | |||
| ecdd3648ae | |||
| 0218f436b2 | |||
| 3c26a4d4f6 | |||
| bedbe34f17 | |||
| 26c822d637 | |||
| 359be442bc | |||
| 6f14c3b0fd | |||
| 8bb0e8c510 | |||
| 27d7f043e7 | |||
| 806476d4b4 | |||
| 5a0db2aea4 | |||
| f8733f59c5 | |||
| 56f351538b | |||
| 36bcfc132d | |||
| 6e245208ac | |||
| a3eb274a3b | |||
| 0ddc6c4bc8 | |||
| eccd6e16e2 | |||
| 3ccec3857e | |||
| 5fd68ae909 | |||
| fb04a85b5a | |||
| 825e190c37 | |||
| 7e60dea257 | |||
| 014e5486a8 | |||
| 33c8b2e963 | |||
| a727d96fe7 | |||
| d3325c176f | |||
| de47061558 | |||
| f1947d1370 | |||
| fba694f61a | |||
| 1c1d02f15a | |||
| cb910f41bd | |||
| e6d3083eea | |||
| b97f8c1145 | |||
| 0c678553af | |||
| 96e740ec22 | |||
| 8affba8be4 | |||
| 81a0729f52 | |||
| e85c7b6f5f | |||
| 33838eae8c | |||
| 1c2e3c754e | |||
| a9a1eecfff | |||
| 95e3971b9c | |||
| 21cf6c974f | |||
| f536a204ba | |||
| 5eb935a079 | |||
| 114d92f93f | |||
| c7be467685 | |||
| 6dc8148372 | |||
| cd3d4ffc9f | |||
| d6c27c0985 | |||
| 9d8280986d | |||
| 8aa8f07cb8 | |||
| 74d8d82b74 | |||
| 397e8223bc | |||
| 604657c1dc | |||
| 9654cbf87e | |||
| fc8ac8956d | |||
| 359a4e5bbd | |||
| 42d2ecca3c | |||
| 56ef23b7d1 | |||
| 339244a1fb | |||
| 6aa984621e | |||
| ba0b5e2120 | |||
| b48e2af2c4 | |||
| 03736cc209 |
@@ -1,19 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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,4 +1,4 @@
|
||||
name: CI for ts-packages
|
||||
name: ci-build-ts
|
||||
|
||||
on:
|
||||
push:
|
||||
+5
-29
@@ -1,4 +1,4 @@
|
||||
name: Build and upload binaries to CI
|
||||
name: ci-build-upload-binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -6,7 +6,6 @@ on:
|
||||
paths:
|
||||
- 'clients/**'
|
||||
- 'common/**'
|
||||
- 'contracts/**'
|
||||
- 'explorer-api/**'
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
@@ -21,7 +20,6 @@ on:
|
||||
paths:
|
||||
- 'clients/**'
|
||||
- 'common/**'
|
||||
- 'contracts/**'
|
||||
- 'explorer-api/**'
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
@@ -44,6 +42,8 @@ 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,27 +63,13 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.69.0
|
||||
toolchain: stable
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
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
|
||||
args: --workspace --release
|
||||
|
||||
- name: Prepare build output
|
||||
shell: bash
|
||||
@@ -99,16 +85,6 @@ 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: Continuous integration
|
||||
name: ci-build
|
||||
|
||||
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
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
- name: Annotate with clippy checks
|
||||
uses: actions-rs/clippy-check@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
|
||||
- name: Run clippy
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
+1
-5
@@ -6,9 +6,5 @@
|
||||
{
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"pull_request"
|
||||
}
|
||||
]
|
||||
]
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
name: Check Contract Schema
|
||||
name: ci-contracts-schema
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'contracts/**'
|
||||
@@ -0,0 +1,79 @@
|
||||
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,4 +1,4 @@
|
||||
name: Contracts
|
||||
name: ci-contracts
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
- 'contracts/**'
|
||||
- 'common/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
paths:
|
||||
- 'contracts/**'
|
||||
- 'common/**'
|
||||
|
||||
@@ -16,18 +16,17 @@ jobs:
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from build_matrix_includes.json
|
||||
# creates the matrix strategy from ci-contracts-matrix-includes.json
|
||||
- uses: actions/checkout@v2
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
inputFile: '.github/workflows/contract_matrix_includes.json'
|
||||
inputFile: '.github/workflows/ci-contracts-matrix-includes.json'
|
||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||
contracts:
|
||||
build:
|
||||
# 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
|
||||
@@ -35,7 +34,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Setup rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -43,25 +43,28 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
- name: Build contracts
|
||||
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
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
- name: Run unit tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --lib --manifest-path contracts/Cargo.toml
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path contracts/Cargo.toml --all -- --check
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --lib --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings
|
||||
@@ -1,4 +1,4 @@
|
||||
name: CI docs
|
||||
name: ci-docs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: CI for linting Typescript
|
||||
name: ci-lint-typescript
|
||||
|
||||
on:
|
||||
push:
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
name: Nym Connect - desktop (Rust)
|
||||
name: ci-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
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
name: CI for nym-connect - Desktop
|
||||
name: ci-nym-connect-desktop
|
||||
|
||||
on:
|
||||
push:
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
name: CI for Network Explorer
|
||||
name: ci-nym-network-explorer
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Nym Wallet (rust)
|
||||
name: ci-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
-1
@@ -1,4 +1,4 @@
|
||||
name: Typescript SDK docs
|
||||
name: ci-sdk-docs-typescript
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Wasm Client
|
||||
name: ci-sdk-wasm
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -9,10 +9,14 @@ on:
|
||||
|
||||
jobs:
|
||||
wasm:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: [custom-runner-linux]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
@@ -31,7 +35,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
|
||||
@@ -0,0 +1,102 @@
|
||||
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
-1
@@ -1,4 +1,4 @@
|
||||
name: check-merge-conflicts
|
||||
name: nightly-check-merge-conflicts
|
||||
|
||||
# Check that the latest release branch merges into master and develop without
|
||||
# any conflicts that git is not able to resolve
|
||||
@@ -0,0 +1,101 @@
|
||||
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
|
||||
@@ -1,176 +0,0 @@
|
||||
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
|
||||
@@ -1,50 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
@@ -1,32 +0,0 @@
|
||||
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}}
|
||||
@@ -1,78 +0,0 @@
|
||||
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
|
||||
@@ -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-wasm
|
||||
run: make contracts
|
||||
|
||||
- name: Upload Mixnet Contract Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Publish SDK to NPM
|
||||
name: Publish Typescript SDK
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -28,8 +28,10 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build and publish
|
||||
- name: Build WASM and Typescript SDK
|
||||
run: yarn sdk:build
|
||||
|
||||
- name: Publish to NPM
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
||||
working-directory: ./sdk/typescript/packages/sdk
|
||||
run: scripts/publish.sh
|
||||
run: ./sdk/typescript/scripts/publish.sh
|
||||
Generated
+279
-223
@@ -690,18 +690,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "0.4.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e"
|
||||
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -925,6 +916,9 @@ name = "bitflags"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
@@ -1834,30 +1828,15 @@ 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 2.2.0",
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -2347,19 +2326,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "4.0.2"
|
||||
version = "5.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.0",
|
||||
@@ -2401,7 +2370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468",
|
||||
"pem-rfc7468 0.6.0",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -2412,6 +2381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468 0.7.0",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -2649,12 +2619,6 @@ 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"
|
||||
@@ -2773,6 +2737,9 @@ name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
@@ -2788,7 +2755,7 @@ dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
"group 0.12.1",
|
||||
"hkdf 0.12.3",
|
||||
"pem-rfc7468",
|
||||
"pem-rfc7468 0.6.0",
|
||||
"pkcs8 0.9.0",
|
||||
"rand_core 0.6.4",
|
||||
"sec1 0.3.0",
|
||||
@@ -2966,6 +2933,17 @@ 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"
|
||||
@@ -3030,7 +3008,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "extension-storage"
|
||||
version = "1.2.0-rc.2"
|
||||
version = "1.2.0-rc.10"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"console_error_panic_hook",
|
||||
@@ -3191,13 +3169,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.10.14"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project",
|
||||
"spin 0.9.8",
|
||||
]
|
||||
|
||||
@@ -3303,13 +3280,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-intrusive"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5"
|
||||
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"lock_api",
|
||||
"parking_lot 0.11.2",
|
||||
"parking_lot 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3658,15 +3635,6 @@ 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"
|
||||
@@ -3695,15 +3663,6 @@ 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"
|
||||
@@ -3982,7 +3941,7 @@ dependencies = [
|
||||
"hyper-rustls",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls 0.22.0",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
@@ -4000,7 +3959,7 @@ dependencies = [
|
||||
"rustls 0.19.1",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls 0.22.0",
|
||||
"tokio-rustls",
|
||||
"webpki 0.21.4",
|
||||
"webpki-roots 0.21.1",
|
||||
]
|
||||
@@ -4492,6 +4451,9 @@ 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"
|
||||
@@ -5339,9 +5301,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.24.2"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
|
||||
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
@@ -5623,7 +5585,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mix-fetch-wasm"
|
||||
version = "1.2.0-rc.2"
|
||||
version = "1.2.0-rc.10"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
@@ -5960,6 +5922,23 @@ 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"
|
||||
@@ -5981,6 +5960,17 @@ 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"
|
||||
@@ -6068,7 +6058,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sqlx 0.6.3",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
@@ -6264,7 +6254,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.4",
|
||||
"cfg-if",
|
||||
"dashmap 5.5.0",
|
||||
"dashmap",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
"gloo-timers",
|
||||
@@ -6289,7 +6279,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.7",
|
||||
"sqlx 0.6.3",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
@@ -6308,7 +6298,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-wasm"
|
||||
version = "1.2.0-rc.2"
|
||||
version = "1.2.0-rc.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -6424,7 +6414,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"log",
|
||||
"sqlx 0.5.11",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -6562,7 +6552,7 @@ dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"clap 4.3.21",
|
||||
"colored",
|
||||
"dashmap 4.0.2",
|
||||
"dashmap",
|
||||
"dirs 4.0.0",
|
||||
"dotenvy",
|
||||
"futures",
|
||||
@@ -6579,6 +6569,7 @@ dependencies = [
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnode-common",
|
||||
"nym-network-defaults",
|
||||
"nym-network-requester",
|
||||
"nym-pemstore",
|
||||
"nym-sphinx",
|
||||
"nym-statistics-common",
|
||||
@@ -6591,7 +6582,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.5.11",
|
||||
"sqlx",
|
||||
"subtle-encoding",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -6879,7 +6870,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.6.3",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
@@ -6900,7 +6891,7 @@ dependencies = [
|
||||
"pretty_env_logger",
|
||||
"rocket",
|
||||
"serde",
|
||||
"sqlx 0.5.11",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -6926,7 +6917,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-tester-wasm"
|
||||
version = "1.2.0-rc.2"
|
||||
version = "1.2.0-rc.10"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
@@ -7115,6 +7106,7 @@ dependencies = [
|
||||
name = "nym-socks5-client-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
"log",
|
||||
@@ -7353,7 +7345,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.5.11",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -7383,6 +7375,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7518,7 +7511,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-wasm-sdk"
|
||||
version = "1.2.0-rc.2"
|
||||
version = "1.2.0-rc.10"
|
||||
dependencies = [
|
||||
"mix-fetch-wasm",
|
||||
"nym-client-wasm",
|
||||
@@ -7734,7 +7727,7 @@ checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"crossbeam-channel",
|
||||
"dashmap 5.5.0",
|
||||
"dashmap",
|
||||
"fnv",
|
||||
"futures-channel",
|
||||
"futures-executor",
|
||||
@@ -7987,6 +7980,15 @@ 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"
|
||||
@@ -8085,6 +8087,17 @@ 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"
|
||||
@@ -9104,6 +9117,28 @@ 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"
|
||||
@@ -9146,16 +9181,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.27.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
|
||||
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.0",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink 0.7.0",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"memchr",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@@ -10038,17 +10072,6 @@ 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"
|
||||
@@ -10062,175 +10085,204 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.5.11"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826"
|
||||
checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33"
|
||||
dependencies = [
|
||||
"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",
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.5.13"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5"
|
||||
checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
"atoi 0.4.0",
|
||||
"bitflags 1.3.2",
|
||||
"ahash 0.8.3",
|
||||
"atoi",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc 2.1.0",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
"event-listener",
|
||||
"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",
|
||||
"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",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"event-listener",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashlink 0.8.3",
|
||||
"hashlink",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"itoa",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
"indexmap 2.0.0",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls 0.20.8",
|
||||
"rustls 0.21.7",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.7",
|
||||
"smallvec",
|
||||
"sqlformat 0.2.1",
|
||||
"sqlx-rt 0.6.3",
|
||||
"stringprep",
|
||||
"sqlformat",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
"webpki-roots 0.22.6",
|
||||
"webpki-roots 0.24.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.5.13"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
|
||||
checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec"
|
||||
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",
|
||||
"sqlx-core",
|
||||
"sqlx-macros-core",
|
||||
"syn 1.0.109",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.6.3"
|
||||
name = "sqlx-macros-core"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9"
|
||||
checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"either",
|
||||
"heck 0.4.1",
|
||||
"hex",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.7",
|
||||
"sqlx-core 0.6.3",
|
||||
"sqlx-rt 0.6.3",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
"syn 1.0.109",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.5.13"
|
||||
name = "sqlx-mysql"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae"
|
||||
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",
|
||||
"tokio",
|
||||
"tokio-rustls 0.22.0",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"rsa",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2 0.10.7",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.6.3"
|
||||
name = "sqlx-postgres"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024"
|
||||
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",
|
||||
"tokio",
|
||||
"tokio-rustls 0.23.4",
|
||||
"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",
|
||||
"libsqlite3-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"sqlx-core",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10331,7 +10383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"crc 3.0.1",
|
||||
"crc",
|
||||
"lazy_static",
|
||||
"md-5",
|
||||
"rand 0.8.5",
|
||||
@@ -10786,17 +10838,6 @@ 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"
|
||||
@@ -11940,6 +11981,15 @@ 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"
|
||||
@@ -12045,7 +12095,7 @@ checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"crc 3.0.1",
|
||||
"crc",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@@ -12096,7 +12146,7 @@ dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"crc 3.0.1",
|
||||
"crc",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"thiserror",
|
||||
@@ -12160,6 +12210,12 @@ 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
@@ -146,6 +146,7 @@ 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"
|
||||
@@ -154,10 +155,12 @@ 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"
|
||||
@@ -175,3 +178,25 @@ 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'
|
||||
|
||||
@@ -1,42 +1,56 @@
|
||||
# Default target
|
||||
# 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.
|
||||
all: test
|
||||
|
||||
test: clippy-all cargo-test contracts-wasm sdk-wasm-test fmt
|
||||
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"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
no-clippy: build cargo-test contracts-wasm fmt fmt-browser-extension-storage
|
||||
# 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
|
||||
|
||||
happy: fmt clippy-happy test
|
||||
# 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:
|
||||
|
||||
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
|
||||
# Not a meta target, more of a top-level target for clippy. Listed here for
|
||||
# visibility. The deps are appended successively.
|
||||
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-happy-$(1):
|
||||
cargo clippy --manifest-path $(2)/Cargo.toml $(3)
|
||||
|
||||
clippy-$(1):
|
||||
cargo clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||
|
||||
clippy-examples-$(1):
|
||||
cargo clippy --manifest-path $(2)/Cargo.toml --workspace --examples -- -D warnings
|
||||
clippy-extra-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace --examples --tests -- -D warnings
|
||||
|
||||
check-$(1):
|
||||
cargo check --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||
@@ -47,30 +61,25 @@ 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-examples-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples
|
||||
build-extra-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples --tests
|
||||
|
||||
build-release-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace --release $(3)
|
||||
$(4) cargo $$($(1)_BUILD_RELEASE_TOOLCHAIN) build --manifest-path $(2)/Cargo.toml --workspace --release $(3)
|
||||
|
||||
fmt-$(1):
|
||||
cargo fmt --manifest-path $(2)/Cargo.toml --all
|
||||
|
||||
clippy-happy: clippy-happy-$(1)
|
||||
clippy: clippy-$(1) clippy-examples-$(1)
|
||||
clippy: clippy-$(1) clippy-extra-$(1)
|
||||
check: check-$(1)
|
||||
cargo-test: test-$(1)
|
||||
cargo-test-expensive: test-expensive-$(1)
|
||||
build: build-$(1) build-examples-$(1)
|
||||
build: build-$(1) build-extra-$(1)
|
||||
build-release-all: build-release-$(1)
|
||||
fmt: fmt-$(1)
|
||||
|
||||
endef
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -80,11 +89,72 @@ 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))
|
||||
#$(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,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,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
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -95,102 +165,14 @@ 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
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl NymConfigTemplate for Config {
|
||||
fn template() -> &'static str {
|
||||
fn template(&self) -> &'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_default(base_data_directory),
|
||||
common_paths: CommonClientPaths::new_base(base_data_directory),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,28 +4,19 @@
|
||||
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::connections::TransmissionLane;
|
||||
use nym_task::TaskManager;
|
||||
use nym_task::TaskHandle;
|
||||
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;
|
||||
|
||||
@@ -92,7 +83,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.catch_interrupt().await;
|
||||
let res = shutdown.wait_for_shutdown().await;
|
||||
log::info!("Stopping nym-client");
|
||||
res
|
||||
}
|
||||
@@ -125,7 +116,7 @@ impl SocketClient {
|
||||
Ok(base_client)
|
||||
}
|
||||
|
||||
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
|
||||
pub async fn start_socket(self) -> Result<TaskHandle, ClientError> {
|
||||
if !self.config.socket.socket_type.is_websocket() {
|
||||
return Err(ClientError::InvalidSocketMode);
|
||||
}
|
||||
@@ -144,141 +135,13 @@ impl SocketClient {
|
||||
client_output,
|
||||
client_state,
|
||||
&self_address,
|
||||
started_client.task_manager.subscribe(),
|
||||
started_client.task_handle.get_handle(),
|
||||
packet_type,
|
||||
);
|
||||
|
||||
info!("Client startup finished!");
|
||||
info!("The address of this client is: {self_address}");
|
||||
|
||||
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!")
|
||||
Ok(started_client.task_handle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ 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::GatewaySetup;
|
||||
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
@@ -114,7 +115,7 @@ impl From<Init> for OverrideConfig {
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InitResults {
|
||||
#[serde(flatten)]
|
||||
client_core: nym_client_core::init::InitResults,
|
||||
client_core: nym_client_core::init::types::InitResults,
|
||||
client_listening_port: u16,
|
||||
client_address: String,
|
||||
}
|
||||
@@ -122,7 +123,11 @@ pub struct InitResults {
|
||||
impl InitResults {
|
||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
Self {
|
||||
client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway),
|
||||
client_core: nym_client_core::init::types::InitResults::new(
|
||||
&config.base,
|
||||
address,
|
||||
gateway,
|
||||
),
|
||||
client_listening_port: config.socket.listening_port,
|
||||
client_address: address.to_string(),
|
||||
}
|
||||
@@ -162,7 +167,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 might overwrite keys!");
|
||||
eprintln!("Instructed to force registering gateway. This will overwrite keys!");
|
||||
}
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
@@ -172,9 +177,10 @@ 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 gateway_setup = GatewaySetup::new_fresh(
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(args.latency_based_selection),
|
||||
false,
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
@@ -186,7 +192,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
|
||||
let network_gateways = if let Some(hardcoded_topology) = args
|
||||
let available_gateways = if let Some(hardcoded_topology) = args
|
||||
.custom_mixnet
|
||||
.map(NymTopology::new_from_file)
|
||||
.transpose()?
|
||||
@@ -198,16 +204,16 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
|
||||
current_gateways(&mut rng, &config.base.client.nym_api_urls).await?
|
||||
};
|
||||
|
||||
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 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 config_save_location = config.default_location();
|
||||
config.save_to_default_location().tap_err(|_| {
|
||||
@@ -222,7 +228,10 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
|
||||
|
||||
eprintln!("Client configuration completed.\n");
|
||||
|
||||
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
|
||||
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
|
||||
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
|
||||
};
|
||||
let init_results = InitResults::new(&config, &address, &gateway_details);
|
||||
println!("{}", args.output.format(&init_results));
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -133,7 +133,7 @@ fn persist_gateway_details(
|
||||
source: Box::new(source),
|
||||
})
|
||||
})?;
|
||||
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
|
||||
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
|
||||
details_store
|
||||
.store_to_disk(&persisted_details)
|
||||
.map_err(|source| {
|
||||
|
||||
@@ -14,8 +14,9 @@ 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::GatewaySetup;
|
||||
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
@@ -113,7 +114,7 @@ impl From<Init> for OverrideConfig {
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InitResults {
|
||||
#[serde(flatten)]
|
||||
client_core: nym_client_core::init::InitResults,
|
||||
client_core: nym_client_core::init::types::InitResults,
|
||||
socks5_listening_port: u16,
|
||||
client_address: String,
|
||||
}
|
||||
@@ -121,7 +122,7 @@ pub struct InitResults {
|
||||
impl InitResults {
|
||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
Self {
|
||||
client_core: nym_client_core::init::InitResults::new(
|
||||
client_core: nym_client_core::init::types::InitResults::new(
|
||||
&config.core.base,
|
||||
address,
|
||||
gateway,
|
||||
@@ -176,9 +177,10 @@ 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 gateway_setup = GatewaySetup::new_fresh(
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(args.latency_based_selection),
|
||||
false,
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
@@ -193,7 +195,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
|
||||
let network_gateways = if let Some(hardcoded_topology) = args
|
||||
let available_gateways = if let Some(hardcoded_topology) = args
|
||||
.custom_mixnet
|
||||
.map(NymTopology::new_from_file)
|
||||
.transpose()?
|
||||
@@ -205,16 +207,16 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
|
||||
current_gateways(&mut rng, &config.core.base.client.nym_api_urls).await?
|
||||
};
|
||||
|
||||
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 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}"))?;
|
||||
|
||||
// TODO: ask the service provider we specified for its interface version and set it in the config
|
||||
|
||||
@@ -229,7 +231,10 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
|
||||
|
||||
let address = init_details.client_address()?;
|
||||
|
||||
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
|
||||
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
|
||||
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
|
||||
};
|
||||
let init_results = InitResults::new(&config, &address, &gateway_details);
|
||||
println!("{}", args.output.format(&init_results));
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -175,7 +175,7 @@ fn persist_gateway_details(
|
||||
source: Box::new(source),
|
||||
})
|
||||
})?;
|
||||
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
|
||||
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
|
||||
details_store
|
||||
.store_to_disk(&persisted_details)
|
||||
.map_err(|source| {
|
||||
|
||||
@@ -62,7 +62,7 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl NymConfigTemplate for Config {
|
||||
fn template() -> &'static str {
|
||||
fn template(&self) -> &'static str {
|
||||
CONFIG_TEMPLATE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_default(base_data_directory),
|
||||
common_paths: CommonClientPaths::new_base(base_data_directory),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,14 @@ 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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ rust-version = "1.66"
|
||||
async-trait = { workspace = true }
|
||||
base64 = "0.21.2"
|
||||
cfg-if = "1.0.0"
|
||||
dashmap = "5.4.0"
|
||||
dashmap = { workspace = true }
|
||||
dirs = "4.0"
|
||||
futures = { workspace = true }
|
||||
humantime-serde = "1.0"
|
||||
@@ -35,7 +35,6 @@ 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" }
|
||||
@@ -58,7 +57,7 @@ features = ["time"]
|
||||
version = "0.14"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
version = "0.6.2"
|
||||
workspace = true
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
optional = true
|
||||
|
||||
@@ -89,7 +88,7 @@ tempfile = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -25,16 +26,18 @@ use crate::client::topology_control::{
|
||||
};
|
||||
use crate::config::{Config, DebugConfig};
|
||||
use crate::error::ClientCoreError;
|
||||
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult};
|
||||
use crate::init::{
|
||||
setup_gateway,
|
||||
types::{GatewayDetails, GatewaySetup, InitialisationResult},
|
||||
};
|
||||
use crate::{config, spawn_future};
|
||||
use futures::channel::mpsc;
|
||||
use log::{debug, info};
|
||||
use log::{debug, error, info};
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_gateway_client::{
|
||||
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
||||
MixnetMessageSender,
|
||||
AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver, PacketRouter,
|
||||
};
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
@@ -42,10 +45,11 @@ 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, TaskManager};
|
||||
use nym_task::{TaskClient, TaskHandle};
|
||||
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;
|
||||
@@ -157,7 +161,12 @@ 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,
|
||||
}
|
||||
|
||||
@@ -175,7 +184,10 @@ 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,
|
||||
}
|
||||
}
|
||||
@@ -186,6 +198,12 @@ 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,
|
||||
@@ -195,6 +213,18 @@ 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,
|
||||
@@ -206,13 +236,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: &InitialisationDetails) -> Recipient {
|
||||
fn mix_address(details: &InitialisationResult) -> 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(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -299,50 +329,37 @@ where
|
||||
config: &Config,
|
||||
initialisation_result: InitialisationResult,
|
||||
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
packet_router: PacketRouter,
|
||||
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.details.managed_keys;
|
||||
let managed_keys = initialisation_result.managed_keys;
|
||||
let GatewayDetails::Configured(gateway_config) = initialisation_result.gateway_details
|
||||
else {
|
||||
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails);
|
||||
};
|
||||
|
||||
let mut gateway_client =
|
||||
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client {
|
||||
existing_client.upgrade(
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
config.debug.gateway_connection.gateway_response_timeout,
|
||||
bandwidth_controller,
|
||||
shutdown,
|
||||
)
|
||||
existing_client.upgrade(packet_router, 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)?;
|
||||
|
||||
let cfg = gateway_config.try_into()?;
|
||||
GatewayClient::new(
|
||||
gateway_address,
|
||||
cfg,
|
||||
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,
|
||||
packet_router,
|
||||
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()
|
||||
@@ -355,11 +372,48 @@ where
|
||||
}
|
||||
})?;
|
||||
|
||||
managed_keys.ensure_gateway_key(shared_key);
|
||||
managed_keys.ensure_gateway_key(Some(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,
|
||||
@@ -387,6 +441,8 @@ 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 =
|
||||
@@ -410,6 +466,32 @@ 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");
|
||||
@@ -424,19 +506,12 @@ 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_client: GatewayClient<C, S::CredentialStore>,
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
shutdown: TaskClient,
|
||||
) -> BatchMixMessageSender
|
||||
where
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
) -> BatchMixMessageSender {
|
||||
info!("Starting mix traffic controller...");
|
||||
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
|
||||
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_transceiver);
|
||||
mix_traffic_controller.start_with_shutdown(shutdown);
|
||||
mix_tx
|
||||
}
|
||||
@@ -473,21 +548,12 @@ 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,
|
||||
overwrite_data,
|
||||
validator_servers,
|
||||
)
|
||||
.await
|
||||
setup_gateway(setup_method, key_store, details_store).await
|
||||
}
|
||||
|
||||
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
|
||||
@@ -505,17 +571,11 @@ 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
|
||||
@@ -536,31 +596,25 @@ where
|
||||
let shared_topology_accessor = TopologyAccessor::new();
|
||||
|
||||
// Shutdown notifier for signalling tasks to stop
|
||||
let task_manager = TaskManager::default();
|
||||
let shutdown = self
|
||||
.shutdown
|
||||
.map(Into::<TaskHandle>::into)
|
||||
.unwrap_or_default()
|
||||
.name_if_unnamed("BaseNymClient");
|
||||
|
||||
// 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.details);
|
||||
let ack_key = init_res.details.managed_keys.ack_key();
|
||||
let encryption_keys = init_res.details.managed_keys.encryption_keypair();
|
||||
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();
|
||||
|
||||
// the components are started in very specific order. Unless you know what you are doing,
|
||||
// do not change that.
|
||||
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 bandwidth_controller = self
|
||||
.dkg_query_client
|
||||
.map(|client| BandwidthController::new(credential_store, client));
|
||||
|
||||
let topology_provider = Self::setup_topology_provider(
|
||||
self.custom_topology_provider.take(),
|
||||
@@ -568,11 +622,36 @@ 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(),
|
||||
task_manager.subscribe(),
|
||||
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"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -582,15 +661,17 @@ where
|
||||
mixnet_messages_receiver,
|
||||
reply_storage.key_storage(),
|
||||
reply_controller_sender.clone(),
|
||||
task_manager.subscribe(),
|
||||
shutdown.fork("received_messages_buffer"),
|
||||
);
|
||||
|
||||
// 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_client, task_manager.subscribe());
|
||||
let message_sender = Self::start_mix_traffic_controller(
|
||||
gateway_transceiver,
|
||||
shutdown.fork("mix_traffic_controller"),
|
||||
);
|
||||
|
||||
// Channels that the websocket listener can use to signal downstream to the real traffic
|
||||
// controller that connections are closed.
|
||||
@@ -617,7 +698,7 @@ where
|
||||
reply_controller_receiver,
|
||||
shared_lane_queue_lengths.clone(),
|
||||
client_connection_rx,
|
||||
task_manager.subscribe(),
|
||||
shutdown.fork("real_traffic_controller"),
|
||||
self.config.debug.traffic.packet_type,
|
||||
);
|
||||
|
||||
@@ -633,7 +714,7 @@ where
|
||||
self_address,
|
||||
shared_topology_accessor.clone(),
|
||||
message_sender,
|
||||
task_manager.subscribe(),
|
||||
shutdown.fork("cover_traffic_stream"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -658,7 +739,7 @@ where
|
||||
reply_controller_sender,
|
||||
topology_accessor: shared_topology_accessor,
|
||||
},
|
||||
task_manager,
|
||||
task_handle: shutdown,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -669,5 +750,5 @@ pub struct BaseClient {
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
|
||||
pub task_manager: TaskManager,
|
||||
pub task_handle: TaskHandle,
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
// 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;
|
||||
@@ -13,19 +17,61 @@ use zeroize::Zeroizing;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait GatewayDetailsStore {
|
||||
pub trait GatewayDetailsStore<T = EmptyCustomDetails> {
|
||||
type StorageError: Error;
|
||||
|
||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError>;
|
||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails<T>, Self::StorageError>
|
||||
where
|
||||
T: DeserializeOwned + Send + Sync;
|
||||
|
||||
async fn store_gateway_details(
|
||||
&self,
|
||||
details: &PersistedGatewayDetails,
|
||||
) -> Result<(), Self::StorageError>;
|
||||
details: &PersistedGatewayDetails<T>,
|
||||
) -> Result<(), Self::StorageError>
|
||||
where
|
||||
T: Serialize + Send + Sync;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PersistedGatewayDetails {
|
||||
#[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 {
|
||||
// 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,
|
||||
@@ -38,13 +84,16 @@ pub struct PersistedGatewayDetails {
|
||||
pub(crate) details: GatewayEndpointConfig,
|
||||
}
|
||||
|
||||
impl From<PersistedGatewayDetails> for GatewayEndpointConfig {
|
||||
fn from(value: PersistedGatewayDetails) -> Self {
|
||||
value.details
|
||||
}
|
||||
#[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 PersistedGatewayDetails {
|
||||
impl PersistedGatewayConfig {
|
||||
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
|
||||
let key_bytes = Zeroizing::new(shared_key.to_bytes());
|
||||
|
||||
@@ -52,7 +101,7 @@ impl PersistedGatewayDetails {
|
||||
key_hasher.update(&key_bytes);
|
||||
let key_hash = key_hasher.finalize().to_vec();
|
||||
|
||||
PersistedGatewayDetails { key_hash, details }
|
||||
PersistedGatewayConfig { key_hash, details }
|
||||
}
|
||||
|
||||
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
|
||||
@@ -66,6 +115,50 @@ impl PersistedGatewayDetails {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -116,7 +209,10 @@ impl OnDiskGatewayDetails {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
|
||||
pub fn load_from_disk<T>(&self) -> Result<PersistedGatewayDetails<T>, OnDiskGatewayDetailsError>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let file = std::fs::File::open(&self.file_location).map_err(|err| {
|
||||
OnDiskGatewayDetailsError::LoadFailure {
|
||||
path: self.file_location.display().to_string(),
|
||||
@@ -127,10 +223,13 @@ impl OnDiskGatewayDetails {
|
||||
Ok(serde_json::from_reader(file)?)
|
||||
}
|
||||
|
||||
pub fn store_to_disk(
|
||||
pub fn store_to_disk<T>(
|
||||
&self,
|
||||
details: &PersistedGatewayDetails,
|
||||
) -> Result<(), OnDiskGatewayDetailsError> {
|
||||
details: &PersistedGatewayDetails<T>,
|
||||
) -> Result<(), OnDiskGatewayDetailsError>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
// 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| {
|
||||
@@ -170,8 +269,8 @@ impl GatewayDetailsStore for OnDiskGatewayDetails {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InMemGatewayDetails {
|
||||
details: Mutex<Option<PersistedGatewayDetails>>,
|
||||
pub struct InMemGatewayDetails<T = EmptyCustomDetails> {
|
||||
details: Mutex<Option<PersistedGatewayDetails<T>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
||||
@@ -38,9 +38,6 @@ 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) => Some(keys.gateway_shared_key()),
|
||||
ManagedKeys::FullyDerived(keys) => keys.gateway_shared_key(),
|
||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
||||
}
|
||||
}
|
||||
@@ -124,10 +124,26 @@ impl ManagedKeys {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_gateway_key(&self, gateway_shared_key: Arc<SharedKeys>) {
|
||||
pub fn ensure_gateway_key(&self, gateway_shared_key: Option<Arc<SharedKeys>>) {
|
||||
if let ManagedKeys::FullyDerived(key_manager) = &self {
|
||||
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|
||||
|| key_manager.gateway_shared_key != gateway_shared_key
|
||||
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
|
||||
{
|
||||
// this should NEVER happen thus panic here
|
||||
panic!("derived fresh gateway shared key whilst already holding one!")
|
||||
@@ -137,12 +153,12 @@ impl ManagedKeys {
|
||||
|
||||
pub async fn deal_with_gateway_key<S: KeyStore>(
|
||||
&mut self,
|
||||
gateway_shared_key: Arc<SharedKeys>,
|
||||
gateway_shared_key: Option<Arc<SharedKeys>>,
|
||||
key_store: &S,
|
||||
) -> Result<(), S::StorageError> {
|
||||
let key_manager = match std::mem::replace(self, ManagedKeys::Invalidated) {
|
||||
ManagedKeys::Initial(keys) => {
|
||||
let key_manager = keys.insert_gateway_shared_key(gateway_shared_key);
|
||||
let key_manager = keys.insert_maybe_gateway_shared_key(gateway_shared_key);
|
||||
key_manager.persist_keys(key_store).await?;
|
||||
key_manager
|
||||
}
|
||||
@@ -184,7 +200,10 @@ impl KeyManagerBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_gateway_shared_key(self, gateway_shared_key: Arc<SharedKeys>) -> KeyManager {
|
||||
pub fn insert_maybe_gateway_shared_key(
|
||||
self,
|
||||
gateway_shared_key: Option<Arc<SharedKeys>>,
|
||||
) -> KeyManager {
|
||||
KeyManager {
|
||||
identity_keypair: self.identity_keypair,
|
||||
encryption_keypair: self.encryption_keypair,
|
||||
@@ -222,7 +241,11 @@ pub struct KeyManager {
|
||||
encryption_keypair: Arc<encryption::KeyPair>,
|
||||
|
||||
/// shared key derived with the gateway during "registration handshake"
|
||||
gateway_shared_key: Arc<SharedKeys>,
|
||||
// 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>>,
|
||||
|
||||
/// key used for producing and processing acknowledgement packets.
|
||||
ack_key: Arc<AckKey>,
|
||||
@@ -232,13 +255,13 @@ impl KeyManager {
|
||||
pub fn from_keys(
|
||||
id_keypair: identity::KeyPair,
|
||||
enc_keypair: encryption::KeyPair,
|
||||
gateway_shared_key: SharedKeys,
|
||||
gateway_shared_key: Option<SharedKeys>,
|
||||
ack_key: AckKey,
|
||||
) -> Self {
|
||||
Self {
|
||||
identity_keypair: Arc::new(id_keypair),
|
||||
encryption_keypair: Arc::new(enc_keypair),
|
||||
gateway_shared_key: Arc::new(gateway_shared_key),
|
||||
gateway_shared_key: gateway_shared_key.map(Arc::new),
|
||||
ack_key: Arc::new(ack_key),
|
||||
}
|
||||
}
|
||||
@@ -265,13 +288,23 @@ 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) -> Arc<SharedKeys> {
|
||||
Arc::clone(&self.gateway_shared_key)
|
||||
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
|
||||
self.gateway_shared_key.as_ref().map(Arc::clone)
|
||||
}
|
||||
|
||||
pub fn remove_gateway_key(self) -> KeyManagerBuilder {
|
||||
if Arc::strong_count(&self.gateway_shared_key) > 1 {
|
||||
if Arc::strong_count(self.must_get_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 keys")
|
||||
self.load_key(self.paths.gateway_shared_key(), "gateway shared")
|
||||
.map(zeroize::Zeroizing::new)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
|
||||
let encryption_paths = self.paths.encryption_key_pair_path();
|
||||
self.load_keypair(encryption_paths, "encryption keys")
|
||||
self.load_keypair(encryption_paths, "encryption")
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn load_identity_keypair(&self) -> Result<identity::KeyPair, OnDiskKeysError> {
|
||||
let identity_paths = self.paths.identity_key_pair_path();
|
||||
self.load_keypair(identity_paths, "identity keys")
|
||||
self.load_keypair(identity_paths, "identity")
|
||||
}
|
||||
|
||||
fn load_key<T: PemStorableKey>(
|
||||
@@ -161,8 +161,9 @@ 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: SharedKeys =
|
||||
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")?;
|
||||
let gateway_shared_key: Option<SharedKeys> = self
|
||||
.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
|
||||
.ok();
|
||||
|
||||
Ok(KeyManager::from_keys(
|
||||
identity_keypair,
|
||||
@@ -173,6 +174,8 @@ 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();
|
||||
|
||||
@@ -188,11 +191,14 @@ impl OnDiskKeys {
|
||||
)?;
|
||||
|
||||
self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?;
|
||||
self.store_key(
|
||||
keys.gateway_shared_key.as_ref(),
|
||||
self.paths.gateway_shared_key(),
|
||||
"gateway shared keys",
|
||||
)?;
|
||||
|
||||
if let Some(shared_keys) = &keys.gateway_shared_key {
|
||||
self.store_key(
|
||||
shared_keys.deref(),
|
||||
self.paths.gateway_shared_key(),
|
||||
"gateway shared keys",
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+33
-20
@@ -1,24 +1,26 @@
|
||||
// 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;
|
||||
|
||||
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>,
|
||||
// that's also disgusting.
|
||||
pub struct Empty;
|
||||
|
||||
pub struct MixTrafficController {
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
|
||||
mix_rx: BatchMixMessageReceiver,
|
||||
|
||||
// TODO: this is temporary work-around.
|
||||
@@ -26,20 +28,31 @@ pub struct MixTrafficController<C, St: Storage> {
|
||||
consecutive_gateway_failure_count: usize,
|
||||
}
|
||||
|
||||
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) {
|
||||
impl MixTrafficController {
|
||||
pub fn new<T>(gateway_transceiver: T) -> (MixTrafficController, BatchMixMessageSender)
|
||||
where
|
||||
T: GatewayTransceiver + Send + 'static,
|
||||
{
|
||||
let (message_sender, message_receiver) =
|
||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||
(
|
||||
MixTrafficController {
|
||||
gateway_client,
|
||||
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,
|
||||
mix_rx: message_receiver,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
},
|
||||
@@ -52,16 +65,16 @@ where
|
||||
|
||||
let result = if mix_packets.len() == 1 {
|
||||
let mix_packet = mix_packets.pop().unwrap();
|
||||
self.gateway_client.send_mix_packet(mix_packet).await
|
||||
self.gateway_transceiver.send_mix_packet(mix_packet).await
|
||||
} else {
|
||||
self.gateway_client
|
||||
self.gateway_transceiver
|
||||
.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
|
||||
@@ -0,0 +1,262 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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.clone();
|
||||
let shutdown_handle = shutdown.fork("acknowledgement_listener");
|
||||
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.clone();
|
||||
let shutdown_handle = shutdown.fork("input_message_listener");
|
||||
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.clone();
|
||||
let shutdown_handle = shutdown.fork("retransmission_request_listener");
|
||||
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.clone();
|
||||
let shutdown_handle = shutdown.fork("sent_notification_listener");
|
||||
spawn_future(async move {
|
||||
sent_notification_listener
|
||||
.run_with_shutdown(shutdown_handle)
|
||||
@@ -293,7 +293,9 @@ where
|
||||
});
|
||||
|
||||
spawn_future(async move {
|
||||
action_controller.run_with_shutdown(shutdown).await;
|
||||
action_controller
|
||||
.run_with_shutdown(shutdown.with_suffix("action_controller"))
|
||||
.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.clone();
|
||||
let shutdown_handle = shutdown.fork("out_queue_control");
|
||||
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.clone();
|
||||
let shutdown_handle = shutdown.fork("reply_control");
|
||||
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, packet_type);
|
||||
ack_control.start_with_shutdown(shutdown.with_suffix("ack_control"), 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.clone();
|
||||
let shutdown_handle = shutdown.fork("fragmented_message_receiver");
|
||||
spawn_future(async move {
|
||||
match fragmented_message_receiver
|
||||
.run_with_shutdown(shutdown_handle)
|
||||
@@ -511,7 +511,9 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
|
||||
}
|
||||
});
|
||||
spawn_future(async move {
|
||||
request_receiver.run_with_shutdown(shutdown).await;
|
||||
request_receiver
|
||||
.run_with_shutdown(shutdown.with_suffix("request_receiver"))
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ impl StorageManager {
|
||||
})?;
|
||||
}
|
||||
|
||||
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(fresh);
|
||||
|
||||
opts.disable_statement_logging();
|
||||
let opts = opts.disable_statement_logging();
|
||||
|
||||
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
|
||||
Ok(pool) => pool,
|
||||
|
||||
@@ -5,10 +5,17 @@ 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;
|
||||
@@ -86,6 +93,54 @@ 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_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
|
||||
pub fn new_base<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_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
|
||||
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
|
||||
let base_dir = base_data_directory.as_ref();
|
||||
|
||||
CommonClientPaths {
|
||||
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
|
||||
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
|
||||
gateway_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
|
||||
keys: ClientKeysPaths::new_default(base_data_directory),
|
||||
keys: ClientKeysPaths::new_base(base_data_directory),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
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},
|
||||
@@ -29,6 +30,8 @@ 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
|
||||
@@ -137,6 +140,17 @@ 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()
|
||||
@@ -230,6 +244,19 @@ 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))]
|
||||
@@ -252,15 +279,29 @@ impl GatewayEndpointConfig {
|
||||
identity::PublicKey::from_base58_string(&self.gateway_id)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
|
||||
fn from(node: nym_topology::gateway::Node) -> GatewayEndpointConfig {
|
||||
let gateway_listener = node.clients_address();
|
||||
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 {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -487,6 +528,11 @@ 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,
|
||||
}
|
||||
@@ -521,6 +567,7 @@ 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,
|
||||
topology_structure: Default::default(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2023 - 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;
|
||||
@@ -19,6 +20,12 @@ pub enum ClientCoreError {
|
||||
source: GatewayClientError,
|
||||
},
|
||||
|
||||
#[error("Custom gateway client error: {source}")]
|
||||
ErasedGatewayClientError {
|
||||
#[from]
|
||||
source: ErasedGatewayError,
|
||||
},
|
||||
|
||||
#[error("Ed25519 error: {0}")]
|
||||
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
|
||||
|
||||
@@ -31,15 +38,9 @@ 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),
|
||||
|
||||
@@ -61,15 +62,6 @@ 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]
|
||||
@@ -122,6 +114,18 @@ 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
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
use crate::config::GatewayEndpointConfig;
|
||||
use crate::error::ClientCoreError;
|
||||
use crate::init::RegistrationResult;
|
||||
use crate::init::types::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};
|
||||
use nym_topology::{filter::VersionFilterable, gateway, mix};
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tungstenite::Message;
|
||||
@@ -24,6 +24,7 @@ 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;
|
||||
|
||||
@@ -77,6 +78,28 @@ 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 {
|
||||
@@ -197,17 +220,27 @@ 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 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,
|
||||
);
|
||||
let mut gateway_client =
|
||||
GatewayClient::new_init(gateway.to_owned().try_into()?, our_identity.clone());
|
||||
|
||||
gateway_client.establish_connection().await.map_err(|err| {
|
||||
log::warn!("Failed to establish connection with gateway!");
|
||||
ClientCoreError::GatewayClientError {
|
||||
@@ -230,6 +263,6 @@ pub(super) async fn register_with_gateway(
|
||||
})?;
|
||||
Ok(RegistrationResult {
|
||||
shared_keys,
|
||||
authenticated_ephemeral_client: Some(gateway_client),
|
||||
authenticated_ephemeral_client: gateway_client,
|
||||
})
|
||||
}
|
||||
|
||||
+139
-372
@@ -8,246 +8,34 @@ use crate::client::base_client::storage::gateway_details::{
|
||||
};
|
||||
use crate::client::key_manager::persistence::KeyStore;
|
||||
use crate::client::key_manager::ManagedKeys;
|
||||
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
|
||||
use crate::{
|
||||
config::{Config, GatewayEndpointConfig},
|
||||
error::ClientCoreError,
|
||||
use crate::config::GatewayEndpointConfig;
|
||||
use crate::error::ClientCoreError;
|
||||
use crate::init::helpers::{
|
||||
choose_gateway_by_latency, get_specified_gateway, uniformly_random_gateway,
|
||||
};
|
||||
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 crate::init::types::{
|
||||
CustomGatewayDetails, GatewayDetails, GatewaySelectionSpecification, GatewaySetup,
|
||||
InitialisationResult,
|
||||
};
|
||||
use nym_gateway_client::client::InitGatewayClient;
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
pub mod types;
|
||||
|
||||
// helpers for error wrapping
|
||||
async fn _store_gateway_details<D>(
|
||||
async fn _store_gateway_details<T, D>(
|
||||
details_store: &D,
|
||||
details: &PersistedGatewayDetails,
|
||||
details: &PersistedGatewayDetails<T>,
|
||||
) -> Result<(), ClientCoreError>
|
||||
where
|
||||
D: GatewayDetailsStore,
|
||||
D: GatewayDetailsStore<T>,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
T: Serialize + Send + Sync,
|
||||
{
|
||||
details_store
|
||||
.store_gateway_details(details)
|
||||
@@ -257,12 +45,13 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
async fn _load_gateway_details<D>(
|
||||
async fn _load_gateway_details<T, D>(
|
||||
details_store: &D,
|
||||
) -> Result<PersistedGatewayDetails, ClientCoreError>
|
||||
) -> Result<PersistedGatewayDetails<T>, ClientCoreError>
|
||||
where
|
||||
D: GatewayDetailsStore,
|
||||
D: GatewayDetailsStore<T>,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
T: DeserializeOwned + Send + Sync,
|
||||
{
|
||||
details_store
|
||||
.load_gateway_details()
|
||||
@@ -284,190 +73,168 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn ensure_valid_details(
|
||||
details: &PersistedGatewayDetails,
|
||||
fn ensure_valid_details<T>(
|
||||
details: &PersistedGatewayDetails<T>,
|
||||
loaded_keys: &ManagedKeys,
|
||||
) -> Result<(), ClientCoreError> {
|
||||
if !details.verify(&loaded_keys.must_get_gateway_shared_key()) {
|
||||
Err(ClientCoreError::MismatchedGatewayDetails {
|
||||
gateway_id: details.details.gateway_id.clone(),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
details.validate(loaded_keys.gateway_shared_key().as_deref())
|
||||
}
|
||||
|
||||
pub async fn setup_gateway_from<K, D>(
|
||||
setup: GatewaySetup,
|
||||
async fn setup_new_gateway<T, K, D>(
|
||||
key_store: &K,
|
||||
details_store: &D,
|
||||
overwrite_data: bool,
|
||||
gateways: Option<&[gateway::Node]>,
|
||||
) -> Result<InitialisationResult, ClientCoreError>
|
||||
selection_specification: GatewaySelectionSpecification<T>,
|
||||
available_gateways: Vec<gateway::Node>,
|
||||
) -> Result<InitialisationResult<T>, ClientCoreError>
|
||||
where
|
||||
K: KeyStore,
|
||||
D: GatewayDetailsStore,
|
||||
D: GatewayDetailsStore<T>,
|
||||
K::StorageError: Send + Sync + 'static,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
T: DeserializeOwned + Serialize + Send + Sync,
|
||||
{
|
||||
// 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),
|
||||
});
|
||||
// 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);
|
||||
}
|
||||
|
||||
let mut rng = OsRng;
|
||||
let mut new_keys = ManagedKeys::generate_new(&mut rng);
|
||||
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
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::RemoteByLatency { must_use_tls } => {
|
||||
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
}
|
||||
GatewaySelectionSpecification::Specified {
|
||||
must_use_tls,
|
||||
identity,
|
||||
} => {
|
||||
let gateway = get_specified_gateway(&identity, &available_gateways)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
}
|
||||
GatewaySelectionSpecification::Custom {
|
||||
gateway_identity,
|
||||
additional_data,
|
||||
} => GatewayDetails::Custom(CustomGatewayDetails::new(gateway_identity, additional_data)),
|
||||
};
|
||||
|
||||
// choose gateway
|
||||
let gateway_details = setup.choose_gateway(gateways.unwrap_or_default()).await?;
|
||||
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
|
||||
};
|
||||
|
||||
// get our identity key
|
||||
let our_identity = managed_keys.identity_keypair();
|
||||
let maybe_shared_keys = registration_result
|
||||
.as_ref()
|
||||
.map(|r| Arc::clone(&r.shared_keys));
|
||||
|
||||
// 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;
|
||||
let persisted_details =
|
||||
PersistedGatewayDetails::new(gateway_details, maybe_shared_keys.as_deref())?;
|
||||
|
||||
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
|
||||
|
||||
// persist gateway keys
|
||||
managed_keys
|
||||
.deal_with_gateway_key(shared_keys, key_store)
|
||||
// persist the keys
|
||||
new_keys
|
||||
.deal_with_gateway_key(maybe_shared_keys, key_store)
|
||||
.await
|
||||
.map_err(|source| ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
})?;
|
||||
|
||||
// persist gateway config
|
||||
// persist gateway configs
|
||||
_store_gateway_details(details_store, &persisted_details).await?;
|
||||
|
||||
Ok(InitialisationResult {
|
||||
details: InitialisationDetails::new(persisted_details.into(), managed_keys),
|
||||
authenticated_ephemeral_client: registration_result.authenticated_ephemeral_client,
|
||||
gateway_details: persisted_details.into(),
|
||||
managed_keys: new_keys,
|
||||
authenticated_ephemeral_client: registration_result
|
||||
.map(|r| r.authenticated_ephemeral_client),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn setup_gateway<K, D>(
|
||||
setup: GatewaySetup,
|
||||
async fn use_loaded_gateway_details<T, K, D>(
|
||||
key_store: &K,
|
||||
details_store: &D,
|
||||
overwrite_data: bool,
|
||||
validator_servers: Option<&[Url]>,
|
||||
) -> Result<InitialisationResult, ClientCoreError>
|
||||
) -> Result<InitialisationResult<T>, ClientCoreError>
|
||||
where
|
||||
K: KeyStore,
|
||||
D: GatewayDetailsStore,
|
||||
D: GatewayDetailsStore<T>,
|
||||
K::StorageError: Send + Sync + 'static,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
T: DeserializeOwned + Send + Sync,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let gateways = current_gateways(&mut rng, validator_servers.unwrap_or_default()).await?;
|
||||
let loaded_details = _load_gateway_details(details_store).await?;
|
||||
let loaded_keys = _load_managed_keys(key_store).await?;
|
||||
|
||||
setup_gateway_from(
|
||||
setup,
|
||||
key_store,
|
||||
details_store,
|
||||
overwrite_data,
|
||||
Some(&gateways),
|
||||
)
|
||||
.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,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
// 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,6 +5,11 @@ 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
|
||||
|
||||
@@ -7,6 +7,7 @@ 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::*;
|
||||
@@ -39,9 +40,23 @@ 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,
|
||||
@@ -69,17 +84,12 @@ 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(
|
||||
gateway_address: String,
|
||||
config: GatewayConfig,
|
||||
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>>,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
response_timeout_duration: Duration,
|
||||
packet_router: PacketRouter,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
shutdown: TaskClient,
|
||||
) -> Self {
|
||||
@@ -87,13 +97,13 @@ impl<C, St> GatewayClient<C, St> {
|
||||
authenticated: false,
|
||||
disabled_credentials_mode: true,
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
gateway_address: config.gateway_listener,
|
||||
gateway_identity: config.gateway_identity,
|
||||
local_identity,
|
||||
shared_key,
|
||||
connection: SocketState::NotConnected,
|
||||
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
|
||||
response_timeout_duration,
|
||||
packet_router,
|
||||
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||
bandwidth_controller,
|
||||
should_reconnect_on_failure: true,
|
||||
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
|
||||
@@ -102,21 +112,34 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) {
|
||||
#[must_use]
|
||||
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
|
||||
self.disabled_credentials_mode = disabled_credentials_mode;
|
||||
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_reconnection_on_failure(mut self, should_reconnect_on_failure: bool) -> Self {
|
||||
self.should_reconnect_on_failure = should_reconnect_on_failure;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_reconnection_attempts(&mut self, reconnection_attempts: usize) {
|
||||
self.reconnection_attempts = reconnection_attempts
|
||||
#[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_backoff(&mut self, backoff: Duration) {
|
||||
self.reconnection_backoff = backoff
|
||||
#[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 gateway_identity(&self) -> identity::PublicKey {
|
||||
@@ -761,16 +784,14 @@ 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(
|
||||
gateway_address: String,
|
||||
gateway_identity: identity::PublicKey,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
response_timeout_duration: Duration,
|
||||
) -> Self {
|
||||
pub fn new_init(config: GatewayConfig, local_identity: Arc<identity::KeyPair>) -> Self {
|
||||
use futures::channel::mpsc;
|
||||
|
||||
// note: this packet_router is completely invalid in normal circumstances, but "works"
|
||||
@@ -784,13 +805,13 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
authenticated: false,
|
||||
disabled_credentials_mode: true,
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
gateway_address: config.gateway_listener,
|
||||
gateway_identity: config.gateway_identity,
|
||||
local_identity,
|
||||
shared_key: None,
|
||||
connection: SocketState::NotConnected,
|
||||
packet_router,
|
||||
response_timeout_duration,
|
||||
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||
bandwidth_controller: None,
|
||||
should_reconnect_on_failure: false,
|
||||
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
|
||||
@@ -801,9 +822,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
|
||||
pub fn upgrade<C, St>(
|
||||
self,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
response_timeout_duration: Duration,
|
||||
packet_router: PacketRouter,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
shutdown: TaskClient,
|
||||
) -> GatewayClient<C, St> {
|
||||
@@ -822,8 +841,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
local_identity: self.local_identity,
|
||||
shared_key: self.shared_key,
|
||||
connection: self.connection,
|
||||
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
|
||||
response_timeout_duration,
|
||||
packet_router,
|
||||
response_timeout_duration: self.response_timeout_duration,
|
||||
bandwidth_controller,
|
||||
should_reconnect_on_failure: self.should_reconnect_on_failure,
|
||||
reconnection_attempts: self.reconnection_attempts,
|
||||
|
||||
@@ -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")]
|
||||
#[error("There was a network error: {0}")]
|
||||
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,6 +76,16 @@ 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 {
|
||||
|
||||
@@ -2,20 +2,23 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
pub use client::GatewayClient;
|
||||
use log::warn;
|
||||
use nym_gateway_requests::BinaryResponse;
|
||||
pub use packet_router::{
|
||||
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
|
||||
};
|
||||
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;
|
||||
|
||||
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,10 +5,8 @@
|
||||
// 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>>>;
|
||||
@@ -37,77 +35,52 @@ impl PacketRouter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn route_received(
|
||||
&mut self,
|
||||
unwrapped_packets: Vec<Vec<u8>>,
|
||||
pub fn route_mixnet_messages(
|
||||
&self,
|
||||
received_messages: Vec<Vec<u8>>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
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 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);
|
||||
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(())
|
||||
}
|
||||
|
||||
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}");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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}");
|
||||
};
|
||||
// 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}");
|
||||
}
|
||||
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,6 +3,7 @@
|
||||
|
||||
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};
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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();
|
||||
|
||||
@@ -33,18 +33,17 @@ pub fn may_get_home() -> Option<PathBuf> {
|
||||
}
|
||||
|
||||
pub trait NymConfigTemplate: Serialize {
|
||||
fn template() -> &'static str;
|
||||
fn template(&self) -> &'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),
|
||||
@@ -64,7 +63,7 @@ where
|
||||
C: NymConfigTemplate,
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
log::trace!("trying to save config file to {}", path.as_ref().display());
|
||||
log::debug!("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
|
||||
@@ -108,7 +107,7 @@ where
|
||||
//
|
||||
//
|
||||
// pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
||||
// fn template() -> &'static str;
|
||||
// fn template(&self) -> &'static str;
|
||||
//
|
||||
// fn config_file_name() -> String {
|
||||
// "config.toml".to_string()
|
||||
|
||||
@@ -13,7 +13,7 @@ thiserror = "1.0"
|
||||
tokio = { version = "1.24.1", features = ["sync"]}
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
version = "0.5"
|
||||
workspace = true
|
||||
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 = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
sqlx = { workspace = true, 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 mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(true);
|
||||
|
||||
opts.disable_statement_logging();
|
||||
let opts = opts.disable_statement_logging();
|
||||
|
||||
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
|
||||
Ok(db) => db,
|
||||
|
||||
@@ -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_default(data_dir);
|
||||
let paths = CommonClientPaths::new_base(data_dir);
|
||||
let db_path = paths.credentials_database;
|
||||
|
||||
nym_credential_storage::initialise_persistent_storage(db_path).await
|
||||
|
||||
@@ -150,6 +150,13 @@ 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;
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
use crate::var_names;
|
||||
use crate::{DenomDetails, ValidatorDetails};
|
||||
|
||||
pub(crate) const NETWORK_NAME: &str = "mainnet";
|
||||
pub const NETWORK_NAME: &str = "mainnet";
|
||||
|
||||
pub(crate) const BECH32_PREFIX: &str = "n";
|
||||
pub 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,13 +15,12 @@ pub const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
|
||||
pub const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||
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 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 const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
|
||||
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
|
||||
|
||||
@@ -6,6 +6,7 @@ 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() -> &'static str {
|
||||
// // fn template(&self) -> &'static str {
|
||||
// // // not intended to be used
|
||||
// // unimplemented!()
|
||||
// // }
|
||||
|
||||
@@ -19,12 +19,13 @@ 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::GatewaySetup;
|
||||
use nym_client_core::init::types::GatewaySetup;
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use nym_task::{TaskClient, TaskHandle};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use std::error::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -44,7 +45,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: TaskManager,
|
||||
pub shutdown_handle: TaskHandle,
|
||||
|
||||
/// Address of the started client
|
||||
pub address: Recipient,
|
||||
@@ -155,7 +156,7 @@ where
|
||||
pub async fn run_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let started = self.start().await?;
|
||||
|
||||
let res = started.shutdown_handle.catch_interrupt().await;
|
||||
let res = started.shutdown_handle.wait_for_shutdown().await;
|
||||
log::info!("Stopping nym-socks5-client");
|
||||
res
|
||||
}
|
||||
@@ -168,7 +169,12 @@ where
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
// Start the main task
|
||||
let started = self.start().await?;
|
||||
let mut shutdown = started.shutdown_handle;
|
||||
let mut shutdown = started
|
||||
.shutdown_handle
|
||||
.try_into_task_manager()
|
||||
.ok_or(anyhow!(
|
||||
"attempted to use `run_and_listen` without owning shutdown handle"
|
||||
))?;
|
||||
|
||||
// Listen to status messages from task, that we forward back to the caller
|
||||
shutdown.start_status_listener(sender).await;
|
||||
@@ -239,7 +245,7 @@ where
|
||||
client_output,
|
||||
client_state,
|
||||
self_address,
|
||||
started_client.task_manager.subscribe(),
|
||||
started_client.task_handle.get_handle(),
|
||||
packet_type,
|
||||
);
|
||||
|
||||
@@ -247,7 +253,7 @@ where
|
||||
info!("The address of this client is: {self_address}");
|
||||
|
||||
Ok(StartedSocks5Client {
|
||||
shutdown_handle: started_client.task_manager,
|
||||
shutdown_handle: started_client.task_handle,
|
||||
address: self_address,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -507,8 +507,8 @@ impl SocksClient {
|
||||
);
|
||||
}
|
||||
|
||||
SocksCommand::Bind => unimplemented!(), // not handled
|
||||
SocksCommand::UdpAssociate => unimplemented!(), // not handled
|
||||
SocksCommand::Bind => return Err(SocksProxyError::BindNotSupported), // not handled
|
||||
SocksCommand::UdpAssociate => return Err(SocksProxyError::UdpNotSupported),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -83,6 +83,12 @@ 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,12 +141,9 @@ impl Controller {
|
||||
}
|
||||
|
||||
fn remove_connection(&mut self, conn_id: ConnectionId) {
|
||||
debug!("Removing {} from controller", conn_id);
|
||||
debug!("Removing {conn_id} from controller");
|
||||
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);
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@ log = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]}
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "chrono"]}
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.24.1", features = [ "time" ] }
|
||||
|
||||
@@ -14,14 +14,18 @@ thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["macros", "sync"] }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
version = "1.24.1"
|
||||
workspace = true
|
||||
features = ["signal", "time", "rt"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
|
||||
version = "0.4"
|
||||
workspace = true
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
version = "0.2.83"
|
||||
workspace = true
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] }
|
||||
|
||||
@@ -7,7 +7,7 @@ pub mod manager;
|
||||
pub mod signal;
|
||||
pub mod spawn;
|
||||
|
||||
pub use manager::{StatusReceiver, StatusSender, TaskClient, TaskManager};
|
||||
pub use manager::{StatusReceiver, StatusSender, TaskClient, TaskHandle, TaskManager};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use signal::wait_for_signal_and_error;
|
||||
|
||||
|
||||
+145
-33
@@ -1,19 +1,22 @@
|
||||
// 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 tokio::{
|
||||
sync::{
|
||||
mpsc,
|
||||
watch::{self, error::SendError},
|
||||
},
|
||||
time::sleep,
|
||||
use std::future::Future;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{error::Error, time::Duration};
|
||||
use tokio::sync::{
|
||||
mpsc,
|
||||
watch::{self, error::SendError},
|
||||
};
|
||||
|
||||
#[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>;
|
||||
@@ -143,6 +146,17 @@ 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(())
|
||||
}
|
||||
@@ -159,9 +173,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");
|
||||
@@ -205,16 +219,10 @@ 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::<()>();
|
||||
|
||||
#[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::<()>();
|
||||
let wait_future = sleep(Duration::from_secs(self.shutdown_timer_secs));
|
||||
|
||||
tokio::select! {
|
||||
_ = self.notify_tx.closed() => {
|
||||
@@ -238,7 +246,10 @@ pub struct TaskClient {
|
||||
name: Option<String>,
|
||||
|
||||
// If a shutdown notification has been registered
|
||||
shutdown: bool,
|
||||
// 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,
|
||||
|
||||
// Listen for shutdown notifications, as well as a mechanism to report back that we have
|
||||
// finished (the receiver is closed).
|
||||
@@ -272,7 +283,7 @@ impl Clone for TaskClient {
|
||||
|
||||
TaskClient {
|
||||
name,
|
||||
shutdown: self.shutdown,
|
||||
shutdown: AtomicBool::new(self.shutdown.load(Ordering::Relaxed)),
|
||||
notify: self.notify.clone(),
|
||||
return_error: self.return_error.clone(),
|
||||
drop_error: self.drop_error.clone(),
|
||||
@@ -286,7 +297,6 @@ 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(
|
||||
@@ -297,7 +307,7 @@ impl TaskClient {
|
||||
) -> TaskClient {
|
||||
TaskClient {
|
||||
name: None,
|
||||
shutdown: false,
|
||||
shutdown: AtomicBool::new(false),
|
||||
notify,
|
||||
return_error,
|
||||
drop_error,
|
||||
@@ -341,6 +351,17 @@ 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>,
|
||||
@@ -360,7 +381,7 @@ impl TaskClient {
|
||||
let (task_status_tx, _task_status_rx) = futures::channel::mpsc::channel(128);
|
||||
TaskClient {
|
||||
name: None,
|
||||
shutdown: false,
|
||||
shutdown: AtomicBool::new(false),
|
||||
notify: notify_rx,
|
||||
return_error: task_halt_tx,
|
||||
drop_error: task_drop_tx,
|
||||
@@ -377,7 +398,7 @@ impl TaskClient {
|
||||
if self.mode.is_dummy() {
|
||||
false
|
||||
} else {
|
||||
self.shutdown
|
||||
self.shutdown.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,11 +406,11 @@ impl TaskClient {
|
||||
if self.mode.is_dummy() {
|
||||
return pending().await;
|
||||
}
|
||||
if self.shutdown {
|
||||
if self.shutdown.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
let _ = self.notify.changed().await;
|
||||
self.shutdown = true;
|
||||
self.shutdown.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub async fn recv_with_delay(&mut self) {
|
||||
@@ -403,29 +424,31 @@ 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;
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::timeout(
|
||||
|
||||
if let Err(timeout) = timeout(
|
||||
Self::SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT,
|
||||
self.recv(),
|
||||
)
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
{
|
||||
self.log(Level::Error, "Task stopped without shutdown called");
|
||||
panic!("{:?}: {timeout}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_shutdown_poll(&mut self) -> bool {
|
||||
pub fn is_shutdown_poll(&self) -> bool {
|
||||
if self.mode.is_dummy() {
|
||||
return false;
|
||||
}
|
||||
if self.shutdown {
|
||||
if self.shutdown.load(Ordering::Relaxed) {
|
||||
return true;
|
||||
}
|
||||
match self.notify.has_changed() {
|
||||
Ok(has_changed) => {
|
||||
if has_changed {
|
||||
self.shutdown = true;
|
||||
self.shutdown.store(true, Ordering::Relaxed);
|
||||
}
|
||||
has_changed
|
||||
}
|
||||
@@ -520,6 +543,95 @@ 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::*;
|
||||
|
||||
@@ -21,6 +21,9 @@ 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 },
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -28,7 +29,7 @@ pub enum GatewayConversionError {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct Node {
|
||||
pub owner: String,
|
||||
pub host: NetworkAddress,
|
||||
@@ -41,6 +42,20 @@ 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()
|
||||
@@ -69,6 +84,10 @@ 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 {
|
||||
|
||||
@@ -33,7 +33,9 @@ pub mod provider_trait;
|
||||
pub(crate) mod serde;
|
||||
|
||||
#[cfg(feature = "serializable")]
|
||||
pub use crate::serde::{SerializableNymTopology, SerializableTopologyError};
|
||||
pub use crate::serde::{
|
||||
SerializableGateway, SerializableMixNode, SerializableNymTopology, SerializableTopologyError,
|
||||
};
|
||||
|
||||
#[cfg(feature = "provider-trait")]
|
||||
pub use provider_trait::{HardcodedTopologyProvider, TopologyProvider};
|
||||
@@ -119,6 +121,17 @@ 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)?;
|
||||
@@ -402,8 +415,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 {}!",
|
||||
bond.mix_node.identity_key, layer
|
||||
"{} says it's on invalid layer {layer}!",
|
||||
bond.mix_node.identity_key
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -414,7 +427,7 @@ pub fn nym_topology_from_detailed(
|
||||
match bond.try_into() {
|
||||
Ok(mix) => layer_entry.push(mix),
|
||||
Err(err) => {
|
||||
warn!("Mix {} / {} is malformed - {err}", mix_id, mix_identity);
|
||||
warn!("Mix {mix_id} / {mix_identity} is malformed: {err}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -426,7 +439,7 @@ pub fn nym_topology_from_detailed(
|
||||
match bond.try_into() {
|
||||
Ok(gate) => gateways.push(gate),
|
||||
Err(err) => {
|
||||
warn!("Gateway {} is malformed - {err}", gate_id);
|
||||
warn!("Gateway {gate_id} is malformed: {err}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -28,7 +29,7 @@ pub enum MixnodeConversionError {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct Node {
|
||||
pub mix_id: MixId,
|
||||
pub owner: String,
|
||||
@@ -42,6 +43,21 @@ 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
Reference in New Issue
Block a user