Compare commits

..

12 Commits

Author SHA1 Message Date
mfahampshire e2b685b73e added info logging 2023-10-23 23:21:28 +02:00
mfahampshire 69b87e8d7c added debug info 2023-10-23 19:09:25 +02:00
mfahampshire 688343ed8b * cleaned up unused imports
* commenting
2023-09-14 21:34:57 +02:00
mfahampshire 5ed8e49cf3 continued 2023-09-14 21:31:22 +02:00
mfahampshire 2a4fe9cc9a changed println! -> info! 2023-09-14 21:31:07 +02:00
mfahampshire cd3dc33707 * using sandbox for identify example
* edited connection.rs to print instead of panic
2023-09-14 20:57:59 +02:00
mfahampshire 466d01eabc temp 2023-09-14 14:26:26 +02:00
mfahampshire 85d2567f97 again increase timeout 2023-09-13 13:26:54 +02:00
mfahampshire 7cbbd352b9 * MyBehaviour networkbehaviour object added
* debug printing
* hit connection.rs panic again: investigating
2023-09-13 12:15:49 +02:00
mfahampshire 56c830cdbd added readme instructions 2023-09-12 17:13:36 +02:00
mfahampshire 46ad61fa9e upped handshake timeout from 5 to 10 secs 2023-09-12 17:00:48 +02:00
mfahampshire 17b22a50fe intermittently working... trying to work out why 2023-09-12 17:00:23 +02:00
492 changed files with 22161 additions and 18202 deletions
@@ -1,4 +1,4 @@
name: ci-build-upload-binaries
name: Build and upload binaries to CI
on:
workflow_dispatch:
@@ -6,6 +6,7 @@ on:
paths:
- 'clients/**'
- 'common/**'
- 'contracts/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
@@ -20,6 +21,7 @@ on:
paths:
- 'clients/**'
- 'common/**'
- 'contracts/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
@@ -42,10 +44,6 @@ jobs:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
env:
CARGO_TERM_COLOR: always
# 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
@@ -65,13 +63,27 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.69.0
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release
args: --workspace --release --all
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.69.0
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install --version 0.112.0 wasm-opt
- name: Build release contracts
run: make contracts-wasm
- name: Prepare build output
shell: bash
@@ -87,6 +99,16 @@ jobs:
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ephemera.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
@@ -20,8 +20,6 @@ jobs:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v3
@@ -1,4 +1,4 @@
name: ci-build-ts
name: CI for ts-packages
on:
push:
@@ -1,4 +1,4 @@
name: ci-build
name: Continuous integration
on:
push:
@@ -37,25 +37,17 @@ on:
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'Cargo.toml'
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [custom-linux, custom-runner-mac-m1]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
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
continue-on-error: true
if: matrix.os == 'custom-linux'
- name: Check out repository code
uses: actions/checkout@v2
@@ -78,40 +70,36 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
# Enable wireguard by default on linux only
args: --workspace --features wireguard
args: --workspace
- name: Build all examples
if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples --features wireguard
args: --workspace --examples
- name: Run all tests
if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features wireguard
args: --workspace
- name: Run expensive tests
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && matrix.os == 'custom-linux'
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 --features wireguard -- --ignored
args: --workspace -- --ignored
- name: Annotate with clippy checks
if: matrix.os == 'custom-linux'
uses: actions-rs/clippy-check@v1
- uses: actions-rs/clippy-check@v1
name: Clippy checks
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --features wireguard
args: --workspace
- name: Clippy
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --features wireguard -- -D warnings
args: --workspace --all-targets -- -D warnings
@@ -0,0 +1,19 @@
[
{
"os":"ubuntu-20.04",
"rust":"stable",
"runOnEvent":"always"
},
{
"os":"windows-latest",
"rust":"stable",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"pull_request"
}
]
@@ -1,7 +1,6 @@
name: ci-contracts-schema
name: Check Contract Schema
on:
workflow_dispatch:
push:
paths:
- 'contracts/**'
@@ -15,8 +14,6 @@ jobs:
check-schema:
name: Generate and check schema
runs-on: custom-runner-linux
env:
CARGO_TERM_COLOR: always
steps:
- name: Check out repository code
uses: actions/checkout@v2
@@ -26,8 +23,9 @@ jobs:
with:
toolchain: stable
- name: Generate the schema
run: make contract-schema
- name: Check for diff
run: git diff --exit-code -- contracts/**/schema
run: git diff --exit-code -- contracts/*/schema
@@ -1,4 +1,4 @@
name: nightly-check-merge-conflicts
name: check-merge-conflicts
# Check that the latest release branch merges into master and develop without
# any conflicts that git is not able to resolve
@@ -1,83 +0,0 @@
name: ci-contracts-upload-binaries
on:
workflow_dispatch:
push:
paths:
- 'common/**'
- 'contracts/**'
pull_request:
paths:
- 'common/**'
- 'contracts/**'
env:
NETWORK: mainnet
jobs:
publish-nym-contracts:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
env:
CARGO_TERM_COLOR: always
# 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
- name: Prepare build output directory
shell: bash
env:
OUTPUT_DIR: ci-contract-builds/${{ github.ref_name }}
run: |
rm -rf ci-contract-builds || true
mkdir -p $OUTPUT_DIR
echo $OUTPUT_DIR
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.69.0
target: wasm32-unknown-unknown
override: true
- name: Install wasm-opt
run: cargo install --version 0.112.0 wasm-opt
- name: Build release contracts
run: make contracts
- name: Prepare build output
shell: bash
env:
OUTPUT_DIR: ci-contract-builds/${{ github.ref_name }}
run: |
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ephemera.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "ci-contract-builds/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
EXCLUDE: "/dist/, /node_modules/"
+1 -1
View File
@@ -1,4 +1,4 @@
name: ci-docs
name: CI docs
on:
workflow_dispatch:
@@ -1,4 +1,4 @@
name: ci-nym-connect-desktop
name: CI for nym-connect - Desktop
on:
push:
@@ -1,4 +1,4 @@
name: ci-nym-connect-desktop-rust
name: Nym Connect - desktop (Rust)
on:
push:
@@ -26,9 +26,7 @@ jobs:
build:
runs-on: [self-hosted, custom-linux]
env:
CARGO_TERM_COLOR: always
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
@@ -6,5 +6,9 @@
{
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"rust":"nightly",
"runOnEvent":"pull_request"
}
]
]
@@ -7,10 +7,10 @@ on:
jobs:
build:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
runs-on: [self-hosted, custom-ubuntu-20.04]
runs-on: [self-hosted, custom-runner-linux]
steps:
- uses: actions/checkout@v2
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -23,7 +23,7 @@ jobs:
run: cargo install --version 0.112.0 wasm-opt
- name: Build release contracts
run: make contracts
run: make contracts-wasm
- name: Upload Mixnet Contract Artifact
uses: actions/upload-artifact@v3
@@ -1,4 +1,4 @@
name: ci-contracts
name: Contracts
on:
push:
@@ -6,7 +6,7 @@ on:
- 'contracts/**'
- 'common/**'
pull_request:
paths:
paths-ignore:
- 'contracts/**'
- 'common/**'
@@ -16,19 +16,18 @@ jobs:
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from ci-contracts-matrix-includes.json
# creates the matrix strategy from build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/ci-contracts-matrix-includes.json'
inputFile: '.github/workflows/contract_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
contracts:
# since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es
runs-on: ubuntu-20.04
env:
CARGO_TERM_COLOR: always
continue-on-error: ${{ matrix.rust == 'nightly' }}
needs: matrix_prep
strategy:
fail-fast: false
@@ -36,8 +35,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Setup rust
uses: actions-rs/toolchain@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
@@ -45,28 +43,25 @@ jobs:
override: true
components: rustfmt, clippy
- name: Build contracts
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
env:
RUSTFLAGS: '-C link-arg=-s'
with:
command: build
args: --manifest-path contracts/Cargo.toml --workspace --lib --target wasm32-unknown-unknown
- name: Run unit tests
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
with:
command: test
args: --lib --manifest-path contracts/Cargo.toml
- name: Check formatting
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path contracts/Cargo.toml --all -- --check
- name: Run clippy
uses: actions-rs/cargo@v1
- uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --lib --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings
@@ -1,4 +1,4 @@
name: ci-nym-network-explorer
name: CI for Network Explorer
on:
workflow_dispatch:
-104
View File
@@ -1,104 +0,0 @@
name: nightly-build
on:
workflow_dispatch:
schedule:
- cron: '14 1 * * *'
jobs:
build:
strategy:
fail-fast: false
matrix:
rust: [stable, beta]
os: [custom-linux, windows10, custom-runner-mac-m1]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
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,102 +0,0 @@
name: nightly-nym-wallet-build
on:
workflow_dispatch:
schedule:
- cron: '14 1 * * *'
defaults:
run:
working-directory: nym-wallet
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [custom-ubuntu-20.04, macos-latest, windows10]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
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
toolchain: stable
override: true
components: rustfmt, clippy
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Unit tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Annotate with clippy warnings
uses: actions-rs/clippy-check@v1
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets -- -D warnings
notification:
needs: build
runs-on: custom-runner-linux
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v3
- name: install npm
uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 18
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
if: env.WORKFLOW_CONCLUSION == 'failure'
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "nym-wallet-nightly-build"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+176
View File
@@ -0,0 +1,176 @@
name: Nightly builds
on:
schedule:
- cron: '14 1 * * *'
jobs:
matrix_prep:
runs-on: ubuntu-20.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v3
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
needs: matrix_prep
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
if: matrix.os == 'ubuntu-20.04'
- name: Check out repository code
uses: actions/checkout@v3
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- name: Build all examples
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace -- --ignored
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- uses: actions-rs/clippy-check@v1
name: Clippy checks
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: build
runs-on: custom-runner-linux
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v3
- name: install npm
uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 18
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
if: env.WORKFLOW_CONCLUSION == 'failure'
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym nightly build"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -0,0 +1,50 @@
[
{
"os":"ubuntu-20.04",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"windows10",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"ubuntu-20.04",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"windows10",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"ubuntu-20.04",
"rust":"nightly",
"runOnEvent":"schedule"
},
{
"os":"windows10",
"rust":"nightly",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"schedule"
}
]
+191
View File
@@ -0,0 +1,191 @@
name: Nightly builds on latest release
on:
schedule:
- cron: '14 2 * * *'
jobs:
matrix_prep:
runs-on: ubuntu-20.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v3
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
get_release:
runs-on: ubuntu-20.04
needs: matrix_prep
outputs:
output1: ${{ steps.step2.outputs.latest_release }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Fetch all branches
run: git fetch --all
- name: Set output variable to latest release branch
id: step2
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+-' | sort -V | tail -n 1 | sed 's/ origin\///')" >> $GITHUB_OUTPUT
build:
needs: [get_release,matrix_prep]
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
if: matrix.os == 'ubuntu-20.04'
- name: Check out latest release branch
uses: actions/checkout@v3
with:
ref: ${{needs.get_release.outputs.output1}}
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- name: Build all examples
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace -- --ignored
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- uses: actions-rs/clippy-check@v1
name: Clippy checks
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: [build,get_release]
runs-on: custom-runner-linux
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v3
- name: install npm
uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 18
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
if: env.WORKFLOW_CONCLUSION == 'failure'
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym nightly build on latest release"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH_NAME: "${{needs.get_release.outputs.output1}}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -0,0 +1,191 @@
name: Nightly builds on second latest release
on:
schedule:
- cron: '24 2 * * *'
jobs:
matrix_prep:
runs-on: ubuntu-20.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v3
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
get_release:
runs-on: ubuntu-20.04
needs: matrix_prep
outputs:
output1: ${{ steps.step2.outputs.latest_release }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Fetch all branches
run: git fetch --all
- name: Set output variable to latest release branch
id: step2
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 2 | head -n 1 | sed 's/ origin\///')" >> $GITHUB_OUTPUT
build:
needs: [get_release,matrix_prep]
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
continue-on-error: true
if: matrix.os == 'ubuntu-20.04'
- name: Check out latest release branch
uses: actions/checkout@v3
with:
ref: ${{needs.get_release.outputs.output1}}
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Build all examples
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace -- --ignored
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- uses: actions-rs/clippy-check@v1
name: Clippy checks
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
with:
command: clean
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: [build,get_release]
runs-on: custom-runner-linux
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v3
- name: install npm
uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 18
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
if: env.WORKFLOW_CONCLUSION == 'failure'
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym nightly build on latest release"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH_NAME: "${{needs.get_release.outputs.output1}}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_NIGHTLY }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-ubuntu-20.04]
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
outputs:
@@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-ubuntu-20.04]
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
outputs:
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-ubuntu-20.04]
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
outputs:
+32
View File
@@ -0,0 +1,32 @@
name: Release Nym Wallet
on:
workflow_dispatch:
inputs:
nym_wallet_version:
description: 'The version of the Nym Wallet to release'
default: '1.0.x'
required: true
type: string
jobs:
create-release:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: Create release
uses: softprops/action-gh-release@v1
with:
body: >-
This is a pre-release
Download the wallet for your platform:
- [Linux](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_amd64_ubuntu20.04.AppImage)
- [MacOS](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_macos_11.dmg)
- [Windows](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_windows.msi)
prerelease: true
name: Nym Wallet v${{ inputs.nym_wallet_version}}
tag_name: nym-wallet-v${{ inputs.nym_wallet_version}}
+78
View File
@@ -0,0 +1,78 @@
name: Webdriverio tests for nym wallet
on:
push:
paths:
- "nym-wallet/**"
defaults:
run:
working-directory: nym-wallet
jobs:
test:
name: wallet tests
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Tauri dependencies
run: >
sudo apt-get update &&
sudo apt-get install -y
libgtk-3-dev
libgtksourceview-3.0-dev
webkit2gtk-4.0
libappindicator3-dev
webkit2gtk-driver
xvfb
continue-on-error: true
- name: Install minimal stable
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- name: Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install yarn for building application
run: yarn install
- name: Build application
run: yarn run webpack:build & yarn run tauri:build
- name: Check binary exists
run: |
cd target/release/
(test -f nym-wallet && echo nym binary exists) || echo wallet does not exist
- name: Install dependencies
run: yarn install
working-directory: nym-wallet/webdriver
- name: Remove existing user datafile
uses: JesseTG/rm@v1.0.2
with:
path: nym-wallet/webdriver/common/data/user-data.json
- name: Create user data json file
id: create-json
uses: jsdaniell/create-json@1.1.2
with:
name: "user-data.json"
json: ${{ secrets.WALLET_USERDATA }}
dir: "nym-wallet/webdriver/common/data/"
- name: Install tauri-driver
uses: actions-rs/cargo@v1
with:
command: install
args: tauri-driver
- name: Launch tests
run: xvfb-run yarn test:runall
working-directory: nym-wallet/webdriver
@@ -12,7 +12,7 @@ on:
jobs:
build:
name: Build APK
runs-on: custom-ubuntu-20.04
runs-on: custom-runner-linux
env:
ANDROID_HOME: ${{ github.workspace }}/android-sdk
NDK_VERSION: 25.2.9519653
@@ -1,4 +1,4 @@
name: ci-sdk-docs-typescript
name: Typescript SDK docs
on:
push:
@@ -1,10 +1,10 @@
name: Publish Typescript SDK
name: Publish SDK to NPM
on:
workflow_dispatch:
jobs:
publish:
runs-on: [custom-ubuntu-20.04]
runs-on: [custom-runner-linux]
steps:
- uses: actions/checkout@v2
@@ -28,10 +28,8 @@ jobs:
- name: Install dependencies
run: yarn
- name: Build WASM and Typescript SDK
run: yarn sdk:build
- name: Publish to NPM
- name: Build and publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
run: ./sdk/typescript/scripts/publish.sh
working-directory: ./sdk/typescript/packages/sdk
run: scripts/publish.sh
@@ -1,4 +1,4 @@
name: ci-lint-typescript
name: CI for linting Typescript
on:
push:
@@ -1,4 +1,4 @@
name: ci-nym-wallet-rust
name: Nym Wallet (rust)
on:
push:
@@ -18,9 +18,7 @@ jobs:
build:
runs-on: [ self-hosted, custom-linux ]
env:
CARGO_TERM_COLOR: always
# env:
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
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
@@ -33,7 +31,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
toolchain: 1.71.0
override: true
components: rustfmt, clippy
@@ -1,4 +1,4 @@
name: ci-sdk-wasm
name: Wasm Client
on:
pull_request:
@@ -9,16 +9,10 @@ on:
jobs:
wasm:
runs-on: [custom-runner-linux]
env:
CARGO_TERM_COLOR: always
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: actions-rs/toolchain@v1
with:
profile: minimal
@@ -37,7 +31,7 @@ jobs:
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
run: cargo install wasm-opt
run: cargo install wasm-opt
- name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli
-25
View File
@@ -3,31 +3,6 @@
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
- add client registry to Gateway ([#3955])
- add HTTP API to Gateway ([#3955])
- add `/client/<pub-key>`, `clients` and `register` routes to the gateway ([#3955])
## [2023.1-milka] (2023-09-24)
- custom Debug impl for mix::Node and gateway::Node ([#3930])
- added forceTls argument to 'MixFetchOptsSimple' ([#3907])
- Enable loop cover traffic by default in NR ([#3904])
- Fix all the cargo warnings ([#3899])
- [Issue] nym-socks5-client crash on UDP request ([#3898])
- Feature/gateway inbuilt nr ([#3877])
- removed queued mixnet migration that was already run ([#3872])
- [feat] Socks5 and Native client: run with hardcoded topology ([#3866])
- Introduce a local network requester directly inside a gateway ([#3838])
[#3930]: https://github.com/nymtech/nym/pull/3930
[#3907]: https://github.com/nymtech/nym/pull/3907
[#3904]: https://github.com/nymtech/nym/pull/3904
[#3899]: https://github.com/nymtech/nym/pull/3899
[#3898]: https://github.com/nymtech/nym/issues/3898
[#3877]: https://github.com/nymtech/nym/pull/3877
[#3872]: https://github.com/nymtech/nym/pull/3872
[#3866]: https://github.com/nymtech/nym/pull/3866
[#3838]: https://github.com/nymtech/nym/issues/3838
## [v1.1.31-kitkat] (2023-09-12)
Generated
+965 -835
View File
File diff suppressed because it is too large Load Diff
-26
View File
@@ -146,7 +146,6 @@ cw2 = { version = "=1.1.0" }
cw3 = { version = "=1.1.0" }
cw4 = { version = "=1.1.0" }
cw-controllers = { version = "=1.1.0" }
dashmap = "5.5.3"
dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7"
@@ -155,7 +154,6 @@ 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"
@@ -164,8 +162,6 @@ tap = "1.0.1"
tendermint-rpc = "0.32" # same version as used by cosmrs
thiserror = "1.0.38"
tokio = "1.24.1"
tokio-tungstenite = "0.20.1"
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
url = "2.4"
zeroize = "1.6.0"
@@ -179,25 +175,3 @@ wasm-bindgen = "0.2.86"
wasm-bindgen-futures = "0.4.37"
wasmtimer = "0.2.0"
web-sys = "0.3.63"
# Profile settings for individual crates
[profile.release.package.nym-socks5-listener]
strip = true
codegen-units = 1
[profile.release.package.nym-client-wasm]
# lto = true
opt-level = 'z'
[profile.release.package.nym-node-tester-wasm]
# lto = true
opt-level = 'z'
[profile.release.package.nym-wasm-sdk]
# lto = true
opt-level = 'z'
[profile.release.package.mix-fetch-wasm]
# lto = true
opt-level = 'z'
+131 -109
View File
@@ -1,85 +1,76 @@
# Top-level Makefile for the nym monorepo
# Default target. Probably what you want to run in normal day-to-day usage when
# you want to check all backend code in one step.
# Default target
all: test
help:
@echo "The main targets are"
@echo " all: the default target. Alias for test"
@echo " build: build all binaries"
@echo " build-release: build platform binaries and contracts in release mode"
@echo " clippy: run clippy for all workspaces"
@echo " test: run clippy, unit tests, and formatting."
@echo " test-all: like test, but also includes the expensive tests"
test: clippy-all cargo-test contracts-wasm sdk-wasm-test fmt
# -----------------------------------------------------------------------------
# Meta targets
# -----------------------------------------------------------------------------
# Run clippy for all workspaces, run all tests, format all Rust code
test: clippy cargo-test fmt
# Same as test, but also runs slow tests
test-all: test cargo-test-expensive
# Build release binaries for the main workspace (platform binaries) and the
# contracts, including running wasm-opt.
# Producing release versions of other components is deferred to their
# respective toolchains.
build-release: build-release-main contracts
no-clippy: build cargo-test contracts-wasm fmt fmt-browser-extension-storage
# Not a meta target, more of a top-level target for building all binaries (in
# debug mode). Listed here for visibility. The deps are appended successively
build:
happy: fmt clippy-happy test
# Not a meta target, more of a top-level target for clippy. Listed here for
# visibility. The deps are appended successively.
clippy:
build: sdk-wasm-build build-browser-extension-storage
# Building release binaries is a little manual as we can't just build --release
# on all workspaces.
build-release: build-release-main contracts-wasm
clippy: sdk-wasm-lint clippy-browser-extension-storage
# Deprecated
# For backwards compatibility
clippy-all: clippy
# -----------------------------------------------------------------------------
# Define targets for a given workspace
# $(1): name
# $(2): path to workspace
# $(3): extra arguments to cargo
# $(4): RUSTFLAGS prefix env
# -----------------------------------------------------------------------------
define add_cargo_workspace
clippy-happy-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml $(3)
clippy-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
clippy-examples-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml --workspace --examples -- -D warnings
check-$(1):
cargo check --manifest-path $(2)/Cargo.toml --workspace $(3)
build-$(1):
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
build-extra-$(1):
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples --tests
build-release-$(1):
$(4) cargo $$($(1)_BUILD_RELEASE_TOOLCHAIN) build --manifest-path $(2)/Cargo.toml --workspace --release $(3)
test-$(1):
cargo test --manifest-path $(2)/Cargo.toml --workspace
test-expensive-$(1):
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
clippy-$(1):
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
build-standalone-$(1):
cargo build --manifest-path $(2)/Cargo.toml $(3)
clippy-extra-$(1):
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace --examples --tests -- -D warnings
build-$(1):
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
build-examples-$(1):
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples
build-release-$(1):
cargo 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)
check: check-$(1)
build: build-$(1) build-extra-$(1)
build-release-all: build-release-$(1)
cargo-test: test-$(1)
cargo-test-expensive: test-expensive-$(1)
clippy: clippy-$(1) clippy-extra-$(1)
build: build-$(1) build-examples-$(1)
build-release-all: build-release-$(1)
fmt: fmt-$(1)
endef
# -----------------------------------------------------------------------------
@@ -89,68 +80,11 @@ endef
# Generate targets for the various cargo workspaces
$(eval $(call add_cargo_workspace,main,.))
$(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unknown-unknown,RUSTFLAGS='-C link-arg=-s'))
$(eval $(call add_cargo_workspace,wallet,nym-wallet))
$(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unknown-unknown))
#$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown))
$(eval $(call add_cargo_workspace,wallet,nym-wallet,))
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
# OVERRIDE: 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
# -----------------------------------------------------------------------------
@@ -161,14 +95,102 @@ build-explorer-api:
build-nym-cli:
cargo build -p nym-cli --release
build-browser-extension-storage:
cargo build -p extension-storage --target wasm32-unknown-unknown
fmt-browser-extension-storage:
cargo fmt -p extension-storage -- --check
clippy-browser-extension-storage:
cargo clippy -p extension-storage --target wasm32-unknown-unknown -- -Dwarnings
sdk-wasm: sdk-wasm-build sdk-wasm-test sdk-wasm-lint
sdk-wasm-build:
# browser storage
$(MAKE) -C nym-browser-extension/storage wasm-pack
# client
$(MAKE) -C wasm/client build
# node-tester
$(MAKE) -C wasm/node-tester build
# mix-fetch
$(MAKE) -C wasm/mix-fetch build
# full
$(MAKE) -C wasm/full-nym-wasm build-full
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
sdk-typescript-build:
lerna run --scope @nymproject/sdk build --stream
lerna run --scope @nymproject/mix-fetch build --stream
lerna run --scope @nymproject/node-tester build --stream
sdk-wasm-test:
# # client
# cargo test -p nym-client-wasm --target wasm32-unknown-unknown
#
# # node-tester
# cargo test -p nym-node-tester-wasm --target wasm32-unknown-unknown
#
# # mix-fetch
# #cargo test -p nym-wasm-sdk --target wasm32-unknown-unknown
#
# # full
# cargo test -p nym-wasm-sdk --target wasm32-unknown-unknown
sdk-wasm-lint:
# client
cargo clippy -p nym-client-wasm --target wasm32-unknown-unknown -- -Dwarnings
# node-tester
cargo clippy -p nym-node-tester-wasm --target wasm32-unknown-unknown -- -Dwarnings
# mix-fetch
$(MAKE) -C wasm/mix-fetch check-fmt
# full
cargo clippy -p nym-wasm-sdk --target wasm32-unknown-unknown -- -Dwarnings
# -----------------------------------------------------------------------------
# Build contracts ready for deploy
# -----------------------------------------------------------------------------
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
VESTING_CONTRACT=$(CONTRACTS_OUT_DIR)/vesting_contract.wasm
MIXNET_CONTRACT=$(CONTRACTS_OUT_DIR)/mixnet_contract.wasm
SERVICE_PROVIDER_DIRECTORY_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_service_provider_directory.wasm
NAME_SERVICE_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_name_service.wasm
contracts-wasm: contracts-wasm-build contracts-wasm-opt
contracts-wasm-build:
RUSTFLAGS='-C link-arg=-s' cargo build --lib --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
contracts-wasm-opt:
wasm-opt --disable-sign-ext -Os $(VESTING_CONTRACT) -o $(VESTING_CONTRACT)
wasm-opt --disable-sign-ext -Os $(MIXNET_CONTRACT) -o $(MIXNET_CONTRACT)
wasm-opt --disable-sign-ext -Os $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) -o $(SERVICE_PROVIDER_DIRECTORY_CONTRACT)
wasm-opt --disable-sign-ext -Os $(NAME_SERVICE_CONTRACT) -o $(NAME_SERVICE_CONTRACT)
contract-schema:
$(MAKE) -C contracts schema
# -----------------------------------------------------------------------------
# Misc
# -----------------------------------------------------------------------------
# NOTE: this seems deprecated an not needed anymore?
mixnet-opt: contracts-wasm
cd contracts/mixnet && make opt
generate-typescript:
cd tools/ts-rs-cli && cargo run && cd ../..
yarn types:lint:fix
run-api-tests:
cd nym-api/tests/functional_test && yarn test:qa
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.30"
version = "1.1.29"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -30,8 +30,8 @@ serde = { workspace = true, features = ["derive"] } # for config serialization/d
serde_json = { workspace = true }
thiserror = { workspace = true }
tap = "1.0.1"
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = { workspace = true }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = "0.14" # websocket
## internal
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
+1 -1
View File
@@ -67,7 +67,7 @@ pub struct Config {
}
impl NymConfigTemplate for Config {
fn template(&self) -> &'static str {
fn template() -> &'static str {
CONFIG_TEMPLATE
}
}
@@ -14,7 +14,7 @@ pub struct ClientPaths {
impl ClientPaths {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
ClientPaths {
common_paths: CommonClientPaths::new_base(base_data_directory),
common_paths: CommonClientPaths::new_default(base_data_directory),
}
}
}
+145 -19
View File
@@ -4,19 +4,27 @@
use crate::client::config::Config;
use crate::error::ClientError;
use crate::websocket;
use futures::channel::mpsc;
use log::*;
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
};
use nym_client_core::client::inbound_messages::InputMessage;
use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
};
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType;
use nym_task::TaskHandle;
use nym_task::connections::TransmissionLane;
use nym_task::TaskManager;
use nym_validator_client::QueryHttpRpcNyxdClient;
use std::error::Error;
use std::path::PathBuf;
use tokio::sync::watch::error::SendError;
pub use nym_sphinx::addressing::clients::Recipient;
pub use nym_sphinx::receiver::ReconstructedMessage;
pub mod config;
@@ -26,17 +34,11 @@ pub struct SocketClient {
/// Client configuration options, including, among other things, packet sending rates,
/// key filepaths, etc.
config: Config,
/// Optional path to a .json file containing standalone network details.
custom_mixnet: Option<PathBuf>,
}
impl SocketClient {
pub fn new(config: Config, custom_mixnet: Option<PathBuf>) -> Self {
SocketClient {
config,
custom_mixnet,
}
pub fn new(config: Config) -> Self {
SocketClient { config }
}
fn start_websocket_listener(
@@ -83,7 +85,7 @@ impl SocketClient {
pub async fn run_socket_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
let shutdown = self.start_socket().await?;
let res = shutdown.wait_for_shutdown().await;
let res = shutdown.catch_interrupt().await;
log::info!("Stopping nym-client");
res
}
@@ -107,16 +109,12 @@ impl SocketClient {
let storage = self.initialise_storage().await?;
let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client);
if let Some(custom_mixnet) = &self.custom_mixnet {
base_client = base_client.with_stored_topology(custom_mixnet)?;
}
let base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client);
Ok(base_client)
}
pub async fn start_socket(self) -> Result<TaskHandle, ClientError> {
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
if !self.config.socket.socket_type.is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
@@ -135,13 +133,141 @@ impl SocketClient {
client_output,
client_state,
&self_address,
started_client.task_handle.get_handle(),
started_client.task_manager.subscribe(),
packet_type,
);
info!("Client startup finished!");
info!("The address of this client is: {self_address}");
Ok(started_client.task_handle)
Ok(started_client.task_manager)
}
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
if self.config.socket.socket_type.is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
let base_builder = self.create_base_client_builder().await?;
let packet_type = self.config.base.debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
let address = started_client.address;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
// register our receiver
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us
client_output
.received_buffer_request_sender
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
reconstructed_sender,
))
.expect("the buffer request failed!");
Ok(DirectClient {
client_input,
_received_buffer_request_sender: client_output.received_buffer_request_sender,
reconstructed_receiver,
address,
shutdown_notifier: started_client.task_manager,
packet_type,
})
}
}
pub struct DirectClient {
client_input: ClientInput,
// make sure to not drop the channel
_received_buffer_request_sender: ReceivedBufferRequestSender,
reconstructed_receiver: ReconstructedMessagesReceiver,
address: Recipient,
// we need to keep reference to this guy otherwise things will start dropping
shutdown_notifier: TaskManager,
packet_type: PacketType,
}
impl DirectClient {
pub fn address(&self) -> &Recipient {
&self.address
}
pub fn signal_shutdown(&self) -> Result<(), SendError<()>> {
self.shutdown_notifier.signal_shutdown()
}
pub async fn wait_for_shutdown(&mut self) {
self.shutdown_notifier.wait_for_shutdown().await
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_regular_message(&mut self, recipient: Recipient, message: Vec<u8>) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_regular(recipient, message, lane, Some(self.packet_type));
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_anonymous_message(
&mut self,
recipient: Recipient,
message: Vec<u8>,
reply_surbs: u32,
) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_anonymous(
recipient,
message,
reply_surbs,
lane,
Some(self.packet_type),
);
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_reply(&mut self, recipient_tag: AnonymousSenderTag, message: Vec<u8>) {
let lane = TransmissionLane::General;
let input_msg =
InputMessage::new_reply(recipient_tag, message, lane, Some(self.packet_type));
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
/// Note: it waits for the first occurrence of messages being sent to ourselves. If you expect multiple
/// messages, you might have to call this function repeatedly.
// TODO: I guess this should really return something that `impl Stream<Item=ReconstructedMessage>`
pub async fn wait_for_messages(&mut self) -> Vec<ReconstructedMessage> {
use futures::StreamExt;
self.reconstructed_receiver
.next()
.await
.expect("buffer controller seems to have somehow died!")
}
}
+18 -52
View File
@@ -15,16 +15,12 @@ use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use nym_topology::NymTopology;
use serde::Serialize;
use std::fmt::Display;
use std::net::IpAddr;
use std::path::PathBuf;
use std::{fs, io};
use tap::TapFallible;
@@ -53,12 +49,7 @@ pub(crate) struct Init {
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the API validators
#[clap(
long,
alias = "api_validators",
value_delimiter = ',',
group = "network"
)]
#[clap(long, alias = "api_validators", value_delimiter = ',')]
// the alias here is included for backwards compatibility (1.1.4 and before)
nym_apis: Option<Vec<url::Url>>,
@@ -74,10 +65,6 @@ pub(crate) struct Init {
#[clap(long)]
host: Option<IpAddr>,
/// Path to .json file containing custom network specification.
#[clap(long, group = "network", hide = true)]
custom_mixnet: Option<PathBuf>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[clap(long, hide = true)]
@@ -115,7 +102,7 @@ impl From<Init> for OverrideConfig {
#[derive(Debug, Serialize)]
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::types::InitResults,
client_core: nym_client_core::init::InitResults,
client_listening_port: u16,
client_address: String,
}
@@ -123,11 +110,7 @@ pub struct InitResults {
impl InitResults {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
client_core: nym_client_core::init::types::InitResults::new(
&config.base,
address,
gateway,
),
client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway),
client_listening_port: config.socket.listening_port,
client_address: address.to_string(),
}
@@ -147,7 +130,7 @@ fn init_paths(id: &str) -> io::Result<()> {
fs::create_dir_all(default_config_directory(id))
}
pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
eprintln!("Initialising client...");
let id = &args.id;
@@ -167,7 +150,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
// re-registering if wanted.
let user_wants_force_register = args.force_register_gateway;
if user_wants_force_register {
eprintln!("Instructed to force registering gateway. This will overwrite keys!");
eprintln!("Instructed to force registering gateway. This might overwrite keys!");
}
// If the client was already initialized, don't generate new keys and don't re-register with
@@ -177,10 +160,9 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway;
let selection_spec = GatewaySelectionSpecification::new(
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
false,
);
// Load and potentially override config
@@ -191,29 +173,16 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let available_gateways = if let Some(hardcoded_topology) = args
.custom_mixnet
.map(NymTopology::new_from_file)
.transpose()?
{
// hardcoded_topology
hardcoded_topology.get_gateways()
} else {
let mut rng = rand::thread_rng();
current_gateways(&mut rng, &config.base.client.nym_api_urls).await?
};
let gateway_setup = GatewaySetup::New {
specification: selection_spec,
available_gateways,
overwrite_data: register_gateway,
};
let init_details =
nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
let init_details = nym_client_core::init::setup_gateway(
gateway_setup,
&key_store,
&details_store,
register_gateway,
Some(&config.base.client.nym_api_urls),
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?
.details;
let config_save_location = config.default_location();
config.save_to_default_location().tap_err(|_| {
@@ -228,10 +197,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
eprintln!("Client configuration completed.\n");
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
};
let init_results = InitResults::new(&config, &address, &gateway_details);
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
println!("{}", args.output.format(&init_results));
Ok(())
+3 -3
View File
@@ -84,8 +84,8 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
let bin_name = "nym-native-client";
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::Init(m) => init::execute(&m).await?,
Commands::Run(m) => run::execute(&m).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
@@ -133,7 +133,7 @@ fn persist_gateway_details(
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
+3 -15
View File
@@ -13,7 +13,6 @@ use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_crypto::asymmetric::identity;
use std::error::Error;
use std::net::IpAddr;
use std::path::PathBuf;
#[derive(Args, Clone)]
pub(crate) struct Run {
@@ -26,12 +25,7 @@ pub(crate) struct Run {
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the API validators
#[clap(
long,
alias = "api_validators",
value_delimiter = ',',
group = "network"
)]
#[clap(long, alias = "api_validators", value_delimiter = ',')]
// the alias here is included for backwards compatibility (1.1.4 and before)
nym_apis: Option<Vec<url::Url>>,
@@ -52,10 +46,6 @@ pub(crate) struct Run {
#[clap(long)]
host: Option<IpAddr>,
/// Path to .json file containing custom network specification.
#[clap(long, group = "network", hide = true)]
custom_mixnet: Option<PathBuf>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[clap(long, hide = true)]
@@ -105,7 +95,7 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("Starting client {}...", args.id);
let mut config = try_load_current_config(&args.id)?;
@@ -116,7 +106,5 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync
return Err(Box::new(ClientError::FailedLocalVersionCheck));
}
SocketClient::new(config, args.custom_mixnet)
.run_socket_forever()
.await
SocketClient::new(config).run_socket_forever().await
}
@@ -230,7 +230,8 @@ impl ServerResponse {
let error_kind = ErrorKind::try_from(b[1])?;
let message_len = u64::from_be_bytes(b[2..2 + size_of::<u64>()].try_into().unwrap());
let message_len =
u64::from_be_bytes(b[2..2 + size_of::<u64>()].as_ref().try_into().unwrap());
let message = &b[2 + size_of::<u64>()..];
if message.len() as u64 != message_len {
return Err(error::Error::new(
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.30"
version = "1.1.29"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -16,7 +16,6 @@ serde_json = { workspace = true }
tap = "1.0.1"
thiserror = { workspace = true }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
rand = "0.7.3"
url = { workspace = true }
# internal
+17 -47
View File
@@ -14,15 +14,11 @@ use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup};
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use nym_topology::NymTopology;
use serde::Serialize;
use std::fmt::Display;
use std::path::PathBuf;
use std::{fs, io};
use tap::TapFallible;
@@ -64,12 +60,7 @@ pub(crate) struct Init {
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the API validators
#[clap(
long,
alias = "api_validators",
value_delimiter = ',',
group = "network"
)]
#[clap(long, alias = "api_validators", value_delimiter = ',')]
// the alias here is included for backwards compatibility (1.1.4 and before)
nym_apis: Option<Vec<url::Url>>,
@@ -77,10 +68,6 @@ pub(crate) struct Init {
#[clap(short, long)]
port: Option<u16>,
/// Path to .json file containing custom network specification.
#[clap(long, group = "network", hide = true)]
custom_mixnet: Option<PathBuf>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[clap(long, hide = true)]
@@ -119,7 +106,7 @@ impl From<Init> for OverrideConfig {
#[derive(Debug, Serialize)]
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::types::InitResults,
client_core: nym_client_core::init::InitResults,
socks5_listening_port: u16,
client_address: String,
}
@@ -127,7 +114,7 @@ pub struct InitResults {
impl InitResults {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
client_core: nym_client_core::init::types::InitResults::new(
client_core: nym_client_core::init::InitResults::new(
&config.core.base,
address,
gateway,
@@ -151,7 +138,7 @@ fn init_paths(id: &str) -> io::Result<()> {
fs::create_dir_all(default_config_directory(id))
}
pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
eprintln!("Initialising client...");
let id = &args.id;
@@ -182,10 +169,9 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway;
let selection_spec = GatewaySelectionSpecification::new(
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
false,
);
// Load and potentially override config
@@ -199,29 +185,16 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let available_gateways = if let Some(hardcoded_topology) = args
.custom_mixnet
.map(NymTopology::new_from_file)
.transpose()?
{
// hardcoded_topology
hardcoded_topology.get_gateways()
} else {
let mut rng = rand::thread_rng();
current_gateways(&mut rng, &config.core.base.client.nym_api_urls).await?
};
let gateway_setup = GatewaySetup::New {
specification: selection_spec,
available_gateways,
overwrite_data: register_gateway,
};
let init_details =
nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
let init_details = nym_client_core::init::setup_gateway(
gateway_setup,
&key_store,
&details_store,
register_gateway,
Some(&config.core.base.client.nym_api_urls),
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?
.details;
// TODO: ask the service provider we specified for its interface version and set it in the config
@@ -236,10 +209,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
let address = init_details.client_address()?;
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
};
let init_results = InitResults::new(&config, &address, &gateway_details);
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
println!("{}", args.output.format(&init_results));
Ok(())
+3 -3
View File
@@ -87,8 +87,8 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
let bin_name = "nym-socks5-client";
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::Init(m) => init::execute(&m).await?,
Commands::Run(m) => run::execute(&m).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
@@ -175,7 +175,7 @@ fn persist_gateway_details(
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
+4 -11
View File
@@ -15,7 +15,6 @@ use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
use nym_crypto::asymmetric::identity;
use nym_socks5_client_core::NymClient;
use nym_sphinx::addressing::clients::Recipient;
use std::path::PathBuf;
#[derive(Args, Clone)]
pub(crate) struct Run {
@@ -46,17 +45,13 @@ pub(crate) struct Run {
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the Nym APIs
#[clap(long, value_delimiter = ',', group = "network")]
#[clap(long, value_delimiter = ',')]
nym_apis: Option<Vec<url::Url>>,
/// Port for the socket to listen on
#[clap(short, long)]
port: Option<u16>,
/// Path to .json file containing custom network specification.
#[clap(long, group = "network", group = "routing", hide = true)]
custom_mixnet: Option<PathBuf>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[clap(long, hide = true)]
@@ -67,7 +62,7 @@ pub(crate) struct Run {
no_cover: bool,
/// Set geo-aware mixnode selection when sending mixnet traffic, for experiments only.
#[clap(long, hide = true, value_parser = validate_country_group, group="routing")]
#[clap(long, hide = true, value_parser = validate_country_group)]
geo_routing: Option<CountryGroup>,
/// Enable medium mixnet traffic, for experiments only.
@@ -129,7 +124,7 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
eprintln!("Starting client {}...", args.id);
let mut config = try_load_current_config(&args.id)?;
@@ -143,7 +138,5 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error +
let storage =
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
.await?;
NymClient::new(config.core, storage, args.custom_mixnet)
.run_forever()
.await
NymClient::new(config.core, storage).run_forever().await
}
+1 -1
View File
@@ -62,7 +62,7 @@ pub struct Config {
}
impl NymConfigTemplate for Config {
fn template(&self) -> &'static str {
fn template() -> &'static str {
CONFIG_TEMPLATE
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ pub struct SocksClientPaths {
impl SocksClientPaths {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
SocksClientPaths {
common_paths: CommonClientPaths::new_base(base_data_directory),
common_paths: CommonClientPaths::new_default(base_data_directory),
}
}
}
@@ -32,14 +32,4 @@ impl OutputFormat {
OutputFormat::Json => serde_json::to_string(data).unwrap(),
}
}
#[cfg(feature = "output_format")]
pub fn to_stdout<T: serde::Serialize + ToString>(&self, data: &T) {
println!("{}", self.format(data))
}
#[cfg(feature = "output_format")]
pub fn to_stderr<T: serde::Serialize + ToString>(&self, data: &T) {
eprintln!("{}", self.format(data))
}
}
+5 -4
View File
@@ -11,7 +11,7 @@ rust-version = "1.66"
async-trait = { workspace = true }
base64 = "0.21.2"
cfg-if = "1.0.0"
dashmap = { workspace = true }
dashmap = "5.4.0"
dirs = "4.0"
futures = { workspace = true }
humantime-serde = "1.0"
@@ -24,7 +24,7 @@ sha2 = "0.10.6"
tap = "1.0.1"
thiserror = { workspace = true }
url = { workspace = true, features = ["serde"] }
tungstenite = { workspace = true, default-features = false }
tungstenite = { version = "0.13.0", default-features = false }
tokio = { workspace = true, features = ["macros"]}
time = "0.3.17"
zeroize = { workspace = true }
@@ -35,11 +35,12 @@ 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" }
nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["serializable"] }
nym-topology = { path = "../topology" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" }
@@ -54,7 +55,7 @@ workspace = true
features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.20.1"
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
version = "0.6.2"
+89 -183
View File
@@ -8,7 +8,6 @@ use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::real_messages_control;
use crate::client::real_messages_control::RealMessagesController;
@@ -26,18 +25,16 @@ use crate::client::topology_control::{
};
use crate::config::{Config, DebugConfig};
use crate::error::ClientCoreError;
use crate::init::{
setup_gateway,
types::{GatewayDetails, GatewaySetup, InitialisationResult},
};
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult};
use crate::{config, spawn_future};
use futures::channel::mpsc;
use log::{debug, error, info};
use log::{debug, info};
use nym_bandwidth_controller::BandwidthController;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::encryption;
use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_client::{
AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver, PacketRouter,
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender,
};
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
@@ -45,12 +42,9 @@ use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_sphinx::params::PacketType;
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::{TaskClient, TaskHandle};
use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug;
use std::path::Path;
use std::sync::Arc;
use url::Url;
@@ -161,12 +155,7 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
config: &'a Config,
client_store: S,
dkg_query_client: Option<C>,
wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
shutdown: Option<TaskClient>,
setup_method: GatewaySetup,
}
@@ -184,27 +173,16 @@ 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,
}
}
#[must_use]
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
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,
provider: Box<dyn TopologyProvider + Send + Sync>,
@@ -213,36 +191,15 @@ 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,
) -> Result<Self, ClientCoreError> {
self.custom_topology_provider =
Some(Box::new(HardcodedTopologyProvider::new_from_file(file)?));
Ok(self)
}
// note: do **NOT** make this method public as its only valid usage is from within `start_base`
// because it relies on the crypto keys being already loaded
fn mix_address(details: &InitialisationResult) -> Recipient {
fn mix_address(details: &InitialisationDetails) -> Recipient {
Recipient::new(
*details.managed_keys.identity_public_key(),
*details.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(details.gateway_details.gateway_id()).unwrap(),
NodeIdentity::from_base58_string(&details.gateway_details.gateway_id).unwrap(),
)
}
@@ -329,37 +286,50 @@ where
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: TaskClient,
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
let managed_keys = initialisation_result.managed_keys;
let GatewayDetails::Configured(gateway_config) = initialisation_result.gateway_details
else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails);
};
let managed_keys = initialisation_result.details.managed_keys;
let mut gateway_client =
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client {
existing_client.upgrade(packet_router, bandwidth_controller, shutdown)
} else {
let cfg = gateway_config.try_into()?;
GatewayClient::new(
cfg,
managed_keys.identity_keypair(),
Some(managed_keys.must_get_gateway_shared_key()),
packet_router,
existing_client.upgrade(
mixnet_message_sender,
ack_sender,
config.debug.gateway_connection.gateway_response_timeout,
bandwidth_controller,
shutdown,
)
} else {
let gateway_config = initialisation_result.details.gateway_details;
let gateway_address = gateway_config.gateway_listener.clone();
let gateway_id = gateway_config.gateway_id;
// TODO: in theory, at this point, this should be infallible
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
GatewayClient::new(
gateway_address,
managed_keys.identity_keypair(),
gateway_identity,
Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender,
ack_sender,
config.debug.gateway_connection.gateway_response_timeout,
bandwidth_controller,
shutdown,
)
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
.with_response_timeout(config.debug.gateway_connection.gateway_response_timeout)
};
let gateway_id = gateway_client.gateway_identity();
gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
let shared_key = gateway_client
.authenticate_and_start()
@@ -372,48 +342,11 @@ where
}
})?;
managed_keys.ensure_gateway_key(Some(shared_key));
managed_keys.ensure_gateway_key(shared_key);
Ok(gateway_client)
}
async fn setup_gateway_transceiver(
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter,
mut shutdown: TaskClient,
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
// if we have setup custom gateway sender and persisted details agree with it, return it
if let Some(mut custom_gateway_transceiver) = custom_gateway_transceiver {
return if !initialisation_result.gateway_details.is_custom() {
Err(ClientCoreError::CustomGatewaySelectionExpected)
} else {
// and make sure to invalidate the task client so we wouldn't cause premature shutdown
shutdown.mark_as_success();
custom_gateway_transceiver.set_packet_router(packet_router)?;
Ok(custom_gateway_transceiver)
};
}
// otherwise, setup normal gateway client, etc
let gateway_client = Self::start_gateway_client(
config,
initialisation_result,
bandwidth_controller,
packet_router,
shutdown,
)
.await?;
Ok(Box::new(RemoteGateway::new(gateway_client)))
}
fn setup_topology_provider(
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
provider_from_config: config::TopologyStructure,
@@ -441,8 +374,6 @@ where
topology_provider: Box<dyn TopologyProvider + Send + Sync>,
topology_config: config::Topology,
topology_accessor: TopologyAccessor,
local_gateway: &NodeIdentity,
wait_for_gateway: bool,
mut shutdown: TaskClient,
) -> Result<(), ClientCoreError> {
let topology_refresher_config =
@@ -466,32 +397,6 @@ where
return Err(ClientCoreError::InsufficientNetworkTopology(err));
}
let gateway_wait_timeout = if wait_for_gateway {
Some(topology_config.max_startup_gateway_waiting_period)
} else {
None
};
if let Err(err) = topology_refresher
.ensure_contains_gateway(local_gateway)
.await
{
if let Some(waiting_timeout) = gateway_wait_timeout {
if let Err(err) = topology_refresher
.wait_for_gateway(local_gateway, waiting_timeout)
.await
{
error!(
"the gateway did not come back online within the specified timeout: {err}"
);
return Err(err.into());
}
} else {
error!("the gateway we're supposedly connected to does not exist. We'll not be able to send any packets to ourselves: {err}");
return Err(err.into());
}
}
if topology_config.disable_refreshing {
// if we're not spawning the refresher, don't cause shutdown immediately
info!("The topology refesher is not going to be started");
@@ -506,12 +411,19 @@ where
Ok(())
}
// controller for sending packets to mixnet (either real traffic or cover traffic)
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
gateway_client: GatewayClient<C, S::CredentialStore>,
shutdown: TaskClient,
) -> BatchMixMessageSender {
) -> BatchMixMessageSender
where
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_transceiver);
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
}
@@ -548,12 +460,21 @@ where
setup_method: GatewaySetup,
key_store: &S::KeyStore,
details_store: &S::GatewayDetailsStore,
overwrite_data: bool,
validator_servers: Option<&[Url]>,
) -> Result<InitialisationResult, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
{
setup_gateway(setup_method, key_store, details_store).await
setup_gateway(
setup_method,
key_store,
details_store,
overwrite_data,
validator_servers,
)
.await
}
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
@@ -571,11 +492,17 @@ where
self.setup_method,
self.client_store.key_store(),
self.client_store.gateway_details_store(),
false,
Some(&self.config.client.nym_api_urls),
)
.await?;
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
let bandwidth_controller = self
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
@@ -596,25 +523,31 @@ where
let shared_topology_accessor = TopologyAccessor::new();
// Shutdown notifier for signalling tasks to stop
let shutdown = self
.shutdown
.map(Into::<TaskHandle>::into)
.unwrap_or_default()
.name_if_unnamed("BaseNymClient");
let task_manager = TaskManager::default();
// channels responsible for dealing with reply-related fun
let (reply_controller_sender, reply_controller_receiver) =
reply_controller::requests::new_control_channels();
let self_address = Self::mix_address(&init_res);
let ack_key = init_res.managed_keys.ack_key();
let encryption_keys = init_res.managed_keys.encryption_keypair();
let self_address = Self::mix_address(&init_res.details);
let ack_key = init_res.details.managed_keys.ack_key();
let encryption_keys = init_res.details.managed_keys.encryption_keypair();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
let bandwidth_controller = self
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
let gateway_client = Self::start_gateway_client(
self.config,
init_res,
bandwidth_controller,
mixnet_messages_sender,
ack_sender,
task_manager.subscribe(),
)
.await?;
let reply_storage =
Self::setup_persistent_reply_storage(reply_storage_backend, task_manager.subscribe())
.await?;
let topology_provider = Self::setup_topology_provider(
self.custom_topology_provider.take(),
@@ -622,36 +555,11 @@ where
self.config.get_nym_api_endpoints(),
);
// needs to be started as the first thing to block if required waiting for the gateway
Self::start_topology_refresher(
topology_provider,
self.config.debug.topology,
shared_topology_accessor.clone(),
self_address.gateway(),
self.wait_for_gateway,
shutdown.fork("topology_refresher"),
)
.await?;
let gateway_packet_router = PacketRouter::new(
ack_sender,
mixnet_messages_sender,
shutdown.get_handle().named("gateway-packet-router"),
);
let gateway_transceiver = Self::setup_gateway_transceiver(
self.custom_gateway_transceiver,
self.config,
init_res,
bandwidth_controller,
gateway_packet_router,
shutdown.fork("gateway_transceiver"),
)
.await?;
let reply_storage = Self::setup_persistent_reply_storage(
reply_storage_backend,
shutdown.fork("persistent_reply_storage"),
task_manager.subscribe(),
)
.await?;
@@ -661,17 +569,15 @@ where
mixnet_messages_receiver,
reply_storage.key_storage(),
reply_controller_sender.clone(),
shutdown.fork("received_messages_buffer"),
task_manager.subscribe(),
);
// The message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let message_sender = Self::start_mix_traffic_controller(
gateway_transceiver,
shutdown.fork("mix_traffic_controller"),
);
let message_sender =
Self::start_mix_traffic_controller(gateway_client, task_manager.subscribe());
// Channels that the websocket listener can use to signal downstream to the real traffic
// controller that connections are closed.
@@ -698,7 +604,7 @@ where
reply_controller_receiver,
shared_lane_queue_lengths.clone(),
client_connection_rx,
shutdown.fork("real_traffic_controller"),
task_manager.subscribe(),
self.config.debug.traffic.packet_type,
);
@@ -714,7 +620,7 @@ where
self_address,
shared_topology_accessor.clone(),
message_sender,
shutdown.fork("cover_traffic_stream"),
task_manager.subscribe(),
);
}
@@ -739,7 +645,7 @@ where
reply_controller_sender,
topology_accessor: shared_topology_accessor,
},
task_handle: shutdown,
task_manager,
})
}
}
@@ -750,5 +656,5 @@ pub struct BaseClient {
pub client_output: ClientOutputStatus,
pub client_state: ClientState,
pub task_handle: TaskHandle,
pub task_manager: TaskManager,
}
@@ -2,12 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::types::{EmptyCustomDetails, GatewayDetails};
use async_trait::async_trait;
use log::error;
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;
@@ -17,57 +13,19 @@ use zeroize::Zeroizing;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewayDetailsStore<T = EmptyCustomDetails> {
pub trait GatewayDetailsStore {
type StorageError: Error;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails<T>, Self::StorageError>
where
T: DeserializeOwned + Send + Sync;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError>;
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails<T>,
) -> Result<(), Self::StorageError>
where
T: Serialize + Send + Sync;
details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PersistedGatewayDetails<T = EmptyCustomDetails> {
/// Standard details of a remote gateway
Default(PersistedGatewayConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(PersistedCustomGatewayDetails<T>),
}
impl<T> PersistedGatewayDetails<T> {
// TODO: this should probably allow for custom verification over T
pub fn validate(&self, shared_key: Option<&SharedKeys>) -> Result<(), ClientCoreError> {
match self {
PersistedGatewayDetails::Default(details) => {
if !details.verify(shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
PersistedGatewayDetails::Custom(_) => {
if shared_key.is_some() {
error!("using custom persisted gateway setup with shared key present - are you sure that's what you want?");
// but technically we could still continue. just ignore the key
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PersistedGatewayConfig {
pub struct PersistedGatewayDetails {
// TODO: should we also verify correctness of the details themselves?
// i.e. we could include a checksum or tag (via the shared keys)
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
@@ -80,16 +38,13 @@ pub struct PersistedGatewayConfig {
pub(crate) details: GatewayEndpointConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistedCustomGatewayDetails<T> {
// whatever custom method is used, gateway's identity must be known
pub gateway_id: String,
#[serde(flatten)]
pub additional_data: T,
impl From<PersistedGatewayDetails> for GatewayEndpointConfig {
fn from(value: PersistedGatewayDetails) -> Self {
value.details
}
}
impl PersistedGatewayConfig {
impl PersistedGatewayDetails {
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
@@ -97,7 +52,7 @@ impl PersistedGatewayConfig {
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize().to_vec();
PersistedGatewayConfig { key_hash, details }
PersistedGatewayDetails { key_hash, details }
}
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
@@ -111,50 +66,6 @@ impl PersistedGatewayConfig {
}
}
impl<T> PersistedGatewayDetails<T> {
pub fn new(
details: GatewayDetails<T>,
shared_key: Option<&SharedKeys>,
) -> Result<Self, ClientCoreError> {
match details {
GatewayDetails::Configured(cfg) => {
let shared_key = shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?;
Ok(PersistedGatewayDetails::Default(
PersistedGatewayConfig::new(cfg, shared_key),
))
}
GatewayDetails::Custom(custom) => Ok(PersistedGatewayDetails::Custom(custom.into())),
}
}
pub fn is_custom(&self) -> bool {
matches!(self, PersistedGatewayDetails::Custom(..))
}
pub fn matches(&self, other: &GatewayDetails<T>) -> bool
where
T: PartialEq,
{
match self {
PersistedGatewayDetails::Default(default) => {
if let GatewayDetails::Configured(other_configured) = other {
&default.details == other_configured
} else {
false
}
}
PersistedGatewayDetails::Custom(custom) => {
if let GatewayDetails::Custom(other_custom) = other {
custom.gateway_id == other_custom.gateway_id
&& custom.additional_data == other_custom.additional_data
} else {
false
}
}
}
}
}
// helper to make Vec<u8> serialization use base64 representation to make it human readable
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
mod base64 {
@@ -205,10 +116,7 @@ impl OnDiskGatewayDetails {
}
}
pub fn load_from_disk<T>(&self) -> Result<PersistedGatewayDetails<T>, OnDiskGatewayDetailsError>
where
T: DeserializeOwned,
{
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
let file = std::fs::File::open(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::LoadFailure {
path: self.file_location.display().to_string(),
@@ -219,13 +127,10 @@ impl OnDiskGatewayDetails {
Ok(serde_json::from_reader(file)?)
}
pub fn store_to_disk<T>(
pub fn store_to_disk(
&self,
details: &PersistedGatewayDetails<T>,
) -> Result<(), OnDiskGatewayDetailsError>
where
T: Serialize,
{
details: &PersistedGatewayDetails,
) -> Result<(), OnDiskGatewayDetailsError> {
// ensure the whole directory structure exists
if let Some(parent_dir) = &self.file_location.parent() {
std::fs::create_dir_all(parent_dir).map_err(|err| {
@@ -265,8 +170,8 @@ impl GatewayDetailsStore for OnDiskGatewayDetails {
}
#[derive(Default)]
pub struct InMemGatewayDetails<T = EmptyCustomDetails> {
details: Mutex<Option<PersistedGatewayDetails<T>>>,
pub struct InMemGatewayDetails {
details: Mutex<Option<PersistedGatewayDetails>>,
}
#[derive(Debug, thiserror::Error)]
@@ -38,6 +38,9 @@ pub trait MixnetClientStorage {
type CredentialStore: CredentialStorage;
type GatewayDetailsStore: GatewayDetailsStore;
// this is a TERRIBLE name...
// fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore, Self::GatewayDetailsStore);
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
fn key_store(&self) -> &Self::KeyStore;
@@ -103,7 +103,7 @@ impl ManagedKeys {
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
match self {
ManagedKeys::Initial(_) => None,
ManagedKeys::FullyDerived(keys) => keys.gateway_shared_key(),
ManagedKeys::FullyDerived(keys) => Some(keys.gateway_shared_key()),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
@@ -124,26 +124,10 @@ impl ManagedKeys {
}
}
pub fn ensure_gateway_key(&self, gateway_shared_key: Option<Arc<SharedKeys>>) {
pub fn ensure_gateway_key(&self, gateway_shared_key: Arc<SharedKeys>) {
if let ManagedKeys::FullyDerived(key_manager) = &self {
if self.gateway_shared_key().is_none() && gateway_shared_key.is_none() {
// the key doesn't exist in either state
return;
}
if gateway_shared_key.is_some() && self.gateway_shared_key().is_none()
|| gateway_shared_key.is_none() && self.gateway_shared_key().is_some()
{
// if one is provided whilst the other is not...
// TODO: should this actually panic or return an error? would this branch be possible
// under normal operation?
panic!("inconsistent re-derived gateway key")
}
// here we know both keys MUST exist
let provided = gateway_shared_key.unwrap();
if !Arc::ptr_eq(key_manager.must_get_gateway_shared_key(), &provided)
|| *key_manager.must_get_gateway_shared_key() != provided
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|| key_manager.gateway_shared_key != gateway_shared_key
{
// this should NEVER happen thus panic here
panic!("derived fresh gateway shared key whilst already holding one!")
@@ -153,12 +137,12 @@ impl ManagedKeys {
pub async fn deal_with_gateway_key<S: KeyStore>(
&mut self,
gateway_shared_key: Option<Arc<SharedKeys>>,
gateway_shared_key: Arc<SharedKeys>,
key_store: &S,
) -> Result<(), S::StorageError> {
let key_manager = match std::mem::replace(self, ManagedKeys::Invalidated) {
ManagedKeys::Initial(keys) => {
let key_manager = keys.insert_maybe_gateway_shared_key(gateway_shared_key);
let key_manager = keys.insert_gateway_shared_key(gateway_shared_key);
key_manager.persist_keys(key_store).await?;
key_manager
}
@@ -200,10 +184,7 @@ impl KeyManagerBuilder {
}
}
pub fn insert_maybe_gateway_shared_key(
self,
gateway_shared_key: Option<Arc<SharedKeys>>,
) -> KeyManager {
pub fn insert_gateway_shared_key(self, gateway_shared_key: Arc<SharedKeys>) -> KeyManager {
KeyManager {
identity_keypair: self.identity_keypair,
encryption_keypair: self.encryption_keypair,
@@ -241,11 +222,7 @@ pub struct KeyManager {
encryption_keypair: Arc<encryption::KeyPair>,
/// shared key derived with the gateway during "registration handshake"
// I'm not a fan of how we broke the nice transition of `KeyManagerBuilder` -> `KeyManager`
// by making this field optional.
// However, it has to be optional for when we use embedded NR inside a gateway,
// since it won't have a shared key (because why would it?)
gateway_shared_key: Option<Arc<SharedKeys>>,
gateway_shared_key: Arc<SharedKeys>,
/// key used for producing and processing acknowledgement packets.
ack_key: Arc<AckKey>,
@@ -255,13 +232,13 @@ impl KeyManager {
pub fn from_keys(
id_keypair: identity::KeyPair,
enc_keypair: encryption::KeyPair,
gateway_shared_key: Option<SharedKeys>,
gateway_shared_key: SharedKeys,
ack_key: AckKey,
) -> Self {
Self {
identity_keypair: Arc::new(id_keypair),
encryption_keypair: Arc::new(enc_keypair),
gateway_shared_key: gateway_shared_key.map(Arc::new),
gateway_shared_key: Arc::new(gateway_shared_key),
ack_key: Arc::new(ack_key),
}
}
@@ -288,23 +265,13 @@ impl KeyManager {
Arc::clone(&self.ack_key)
}
fn must_get_gateway_shared_key(&self) -> &Arc<SharedKeys> {
self.gateway_shared_key
.as_ref()
.expect("gateway shared key is unavailable")
}
pub fn uses_custom_gateway(&self) -> bool {
self.gateway_shared_key.is_none()
}
/// Gets an atomically reference counted pointer to [`SharedKey`].
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
self.gateway_shared_key.as_ref().map(Arc::clone)
pub fn gateway_shared_key(&self) -> Arc<SharedKeys> {
Arc::clone(&self.gateway_shared_key)
}
pub fn remove_gateway_key(self) -> KeyManagerBuilder {
if Arc::strong_count(self.must_get_gateway_shared_key()) > 1 {
if Arc::strong_count(&self.gateway_shared_key) > 1 {
panic!("attempted to remove gateway key whilst still holding multiple references!")
}
KeyManagerBuilder {
@@ -88,20 +88,20 @@ impl OnDiskKeys {
pub fn ephemeral_load_gateway_keys(
&self,
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
self.load_key(self.paths.gateway_shared_key(), "gateway shared")
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
.map(zeroize::Zeroizing::new)
}
#[doc(hidden)]
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
let encryption_paths = self.paths.encryption_key_pair_path();
self.load_keypair(encryption_paths, "encryption")
self.load_keypair(encryption_paths, "encryption keys")
}
#[doc(hidden)]
pub fn load_identity_keypair(&self) -> Result<identity::KeyPair, OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path();
self.load_keypair(identity_paths, "identity")
self.load_keypair(identity_paths, "identity keys")
}
fn load_key<T: PemStorableKey>(
@@ -161,9 +161,8 @@ impl OnDiskKeys {
let encryption_keypair = self.load_encryption_keypair()?;
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
let gateway_shared_key: Option<SharedKeys> = self
.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
.ok();
let gateway_shared_key: SharedKeys =
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")?;
Ok(KeyManager::from_keys(
identity_keypair,
@@ -174,8 +173,6 @@ impl OnDiskKeys {
}
fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> {
use std::ops::Deref;
let identity_paths = self.paths.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path();
@@ -191,14 +188,11 @@ impl OnDiskKeys {
)?;
self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?;
if let Some(shared_keys) = &keys.gateway_shared_key {
self.store_key(
shared_keys.deref(),
self.paths.gateway_shared_key(),
"gateway shared keys",
)?;
}
self.store_key(
keys.gateway_shared_key.as_ref(),
self.paths.gateway_shared_key(),
"gateway shared keys",
)?;
Ok(())
}
@@ -1,26 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
use crate::spawn_future;
use log::*;
use nym_credential_storage::storage::Storage;
use nym_gateway_client::GatewayClient;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
pub mod transceiver;
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
const MAX_FAILURE_COUNT: usize = 100;
// that's also disgusting.
pub struct Empty;
pub struct MixTrafficController {
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
pub struct MixTrafficController<C, St: Storage> {
// TODO: most likely to be replaced by some higher level construct as
// later on gateway_client will need to be accessible by other entities
gateway_client: GatewayClient<C, St>,
mix_rx: BatchMixMessageReceiver,
// TODO: this is temporary work-around.
@@ -28,31 +26,20 @@ pub struct MixTrafficController {
consecutive_gateway_failure_count: usize,
}
impl MixTrafficController {
pub fn new<T>(gateway_transceiver: T) -> (MixTrafficController, BatchMixMessageSender)
where
T: GatewayTransceiver + Send + 'static,
{
impl<C, St> MixTrafficController<C, St>
where
C: DkgQueryClient + Sync + Send + 'static,
St: Storage + 'static,
<St as Storage>::StorageError: Send + Sync + 'static,
{
pub fn new(
gateway_client: GatewayClient<C, St>,
) -> (MixTrafficController<C, St>, BatchMixMessageSender) {
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
MixTrafficController {
gateway_transceiver: Box::new(gateway_transceiver),
mix_rx: message_receiver,
consecutive_gateway_failure_count: 0,
},
message_sender,
)
}
pub fn new_dynamic(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
) -> (MixTrafficController, BatchMixMessageSender) {
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
MixTrafficController {
gateway_transceiver,
gateway_client,
mix_rx: message_receiver,
consecutive_gateway_failure_count: 0,
},
@@ -65,16 +52,16 @@ impl MixTrafficController {
let result = if mix_packets.len() == 1 {
let mix_packet = mix_packets.pop().unwrap();
self.gateway_transceiver.send_mix_packet(mix_packet).await
self.gateway_client.send_mix_packet(mix_packet).await
} else {
self.gateway_transceiver
self.gateway_client
.batch_send_mix_packets(mix_packets)
.await
};
match result {
Err(err) => {
error!("Failed to send sphinx packet(s) to the gateway: {err}");
error!("Failed to send sphinx packet(s) to the gateway! - {err}");
self.consecutive_gateway_failure_count += 1;
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// todo: in the future this should initiate a 'graceful' shutdown or try
@@ -1,262 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use log::{debug, error};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient;
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
use nym_sphinx::forwarding::packet::MixPacket;
use std::fmt::Debug;
use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))]
use futures::channel::{mpsc, oneshot};
// we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch
#[derive(Debug, Error)]
#[error(transparent)]
pub struct ErasedGatewayError(Box<dyn std::error::Error + Send + Sync>);
fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGatewayError {
ErasedGatewayError(Box::new(err))
}
/// This combines combines the functionalities of being able to send and receive mix packets.
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
fn gateway_identity(&self) -> identity::PublicKey;
}
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
/// usually through a gateway.
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewaySender {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError>;
async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
// allow for optimisation when sending multiple packets
for packet in packets {
self.send_mix_packet(packet).await?;
}
Ok(())
}
}
/// this trait defines the functionality of being able to correctly route
/// packets received from the mixnet, i.e. acks and 'proper' messages.
pub trait GatewayReceiver {
// ughhhh I really dislike this method, but couldn't come up wih anything better
// ideally this would have been an associated type, but heh. we can't.
fn set_packet_router(
&mut self,
_packet_router: PacketRouter,
) -> Result<(), ErasedGatewayError> {
debug!("no-op packet router setup");
Ok(())
}
}
// to allow for dynamic dispatch
impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
#[inline]
fn gateway_identity(&self) -> identity::PublicKey {
(**self).gateway_identity()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<G: GatewaySender + ?Sized + Send> GatewaySender for Box<G> {
#[inline]
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
(**self).send_mix_packet(packet).await
}
#[inline]
async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
(**self).batch_send_mix_packets(packets).await
}
}
impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> {
#[inline]
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
(**self).set_packet_router(packet_router)
}
}
/// Gateway to which the client is connected through a socket.
/// Most likely through a websocket.
pub struct RemoteGateway<C, St> {
gateway_client: GatewayClient<C, St>,
}
impl<C, St> RemoteGateway<C, St> {
pub fn new(gateway_client: GatewayClient<C, St>) -> Self {
Self { gateway_client }
}
}
impl<C, St> GatewayTransceiver for RemoteGateway<C, St>
where
C: Send,
St: Send,
{
fn gateway_identity(&self) -> identity::PublicKey {
self.gateway_client.gateway_identity()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C, St> GatewaySender for RemoteGateway<C, St>
where
C: Send,
St: Send,
{
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.gateway_client
.send_mix_packet(packet)
.await
.map_err(erase_err)
}
async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
self.gateway_client
.batch_send_mix_packets(packets)
.await
.map_err(erase_err)
}
}
impl<C, St> GatewayReceiver for RemoteGateway<C, St> {}
#[derive(Debug, Error)]
pub enum LocalGatewayError {
#[error("attempted to set the packet router for the second time")]
PacketRouterAlreadySet,
#[error("failed to setup packet router - has the receiver been dropped?")]
FailedPacketRouterSetup,
}
/// Gateway running within the same process.
#[cfg(not(target_arch = "wasm32"))]
pub struct LocalGateway {
/// Identity of the locally managed gateway
local_identity: identity::PublicKey,
// 'sender' part
/// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers.
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
// 'receiver' part
packet_router_tx: Option<oneshot::Sender<PacketRouter>>,
}
#[cfg(not(target_arch = "wasm32"))]
impl LocalGateway {
pub fn new(
local_identity: identity::PublicKey,
packet_forwarder: mpsc::UnboundedSender<MixPacket>,
packet_router_tx: oneshot::Sender<PacketRouter>,
) -> Self {
LocalGateway {
local_identity,
packet_forwarder,
packet_router_tx: Some(packet_router_tx),
}
}
}
#[cfg(not(target_arch = "wasm32"))]
mod nonwasm_sealed {
use super::*;
impl GatewayTransceiver for LocalGateway {
fn gateway_identity(&self) -> identity::PublicKey {
self.local_identity
}
}
#[async_trait]
impl GatewaySender for LocalGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.packet_forwarder
.unbounded_send(packet)
.map_err(|err| err.into_send_error())
.map_err(erase_err)
}
}
impl GatewayReceiver for LocalGateway {
fn set_packet_router(
&mut self,
packet_router: PacketRouter,
) -> Result<(), ErasedGatewayError> {
let Some(packet_routex_tx) = self.packet_router_tx.take() else {
return Err(erase_err(LocalGatewayError::PacketRouterAlreadySet));
};
packet_routex_tx
.send(packet_router)
.map_err(|_| erase_err(LocalGatewayError::FailedPacketRouterSetup))
}
}
}
// if we ever decided to start writing unit tests... : )
pub struct MockGateway {
dummy_identity: identity::PublicKey,
packet_router: Option<PacketRouter>,
sent: Vec<MixPacket>,
}
impl Default for MockGateway {
fn default() -> Self {
MockGateway {
dummy_identity: "3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7"
.parse()
.unwrap(),
packet_router: None,
sent: vec![],
}
}
}
#[derive(Debug, Error)]
#[error("mock gateway error")]
pub struct MockGatewayError;
impl GatewayReceiver for MockGateway {
// TODO: that's frustrating. can't do anything about the behaviour here since all the routing is in the `PacketRouter`...
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
self.packet_router = Some(packet_router);
Ok(())
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewaySender for MockGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.sent.push(packet);
Ok(())
}
}
impl GatewayTransceiver for MockGateway {
fn gateway_identity(&self) -> identity::PublicKey {
self.dummy_identity
}
}
@@ -71,7 +71,7 @@ impl AcknowledgementListener {
while !shutdown.is_shutdown() {
tokio::select! {
acks = self.ack_receiver.next() => match acks {
Some(acks) => self.handle_ack_receiver_item(acks).await,
Some(acks) => {self.handle_ack_receiver_item(acks).await}
None => {
log::trace!("AcknowledgementListener: Stopping since channel closed");
break;
@@ -260,7 +260,7 @@ where
let mut sent_notification_listener = self.sent_notification_listener;
let mut action_controller = self.action_controller;
let shutdown_handle = shutdown.fork("acknowledgement_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
acknowledgement_listener
.run_with_shutdown(shutdown_handle)
@@ -268,7 +268,7 @@ where
debug!("The acknowledgement listener has finished execution!");
});
let shutdown_handle = shutdown.fork("input_message_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
input_message_listener
.run_with_shutdown(shutdown_handle)
@@ -276,7 +276,7 @@ where
debug!("The input listener has finished execution!");
});
let shutdown_handle = shutdown.fork("retransmission_request_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
retransmission_request_listener
.run_with_shutdown(shutdown_handle, packet_type)
@@ -284,7 +284,7 @@ where
debug!("The retransmission request listener has finished execution!");
});
let shutdown_handle = shutdown.fork("sent_notification_listener");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
sent_notification_listener
.run_with_shutdown(shutdown_handle)
@@ -293,9 +293,7 @@ where
});
spawn_future(async move {
action_controller
.run_with_shutdown(shutdown.with_suffix("action_controller"))
.await;
action_controller.run_with_shutdown(shutdown).await;
debug!("The controller has finished execution!");
});
}
@@ -213,17 +213,17 @@ impl RealMessagesController<OsRng> {
let ack_control = self.ack_control;
let mut reply_control = self.reply_control;
let shutdown_handle = shutdown.fork("out_queue_control");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
out_queue_control.run_with_shutdown(shutdown_handle).await;
debug!("The out queue controller has finished execution!");
});
let shutdown_handle = shutdown.fork("reply_control");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
reply_control.run_with_shutdown(shutdown_handle).await;
debug!("The reply controller has finished execution!");
});
ack_control.start_with_shutdown(shutdown.with_suffix("ack_control"), packet_type);
ack_control.start_with_shutdown(shutdown, packet_type);
}
}
@@ -500,7 +500,7 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver;
let shutdown_handle = shutdown.fork("fragmented_message_receiver");
let shutdown_handle = shutdown.clone();
spawn_future(async move {
match fragmented_message_receiver
.run_with_shutdown(shutdown_handle)
@@ -511,9 +511,7 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
}
});
spawn_future(async move {
request_receiver
.run_with_shutdown(shutdown.with_suffix("request_receiver"))
.await;
request_receiver.run_with_shutdown(shutdown).await;
});
}
}
@@ -199,7 +199,7 @@ fn group_mixnodes_by_country_code(
if let Some(ref location) = m.location {
let country_code = location.two_letter_iso_country_code.clone();
let group_code = CountryGroup::new(country_code.as_str());
let mixnodes = acc.entry(group_code).or_default();
let mixnodes = acc.entry(group_code).or_insert_with(Vec::new);
mixnodes.push(m.mix_id);
}
acc
@@ -5,17 +5,10 @@ use crate::spawn_future;
pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
use futures::StreamExt;
use log::*;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::NymTopologyError;
use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep;
mod accessor;
pub mod geo_aware_provider;
pub(crate) mod nym_api_provider;
@@ -93,54 +86,6 @@ impl TopologyRefresher {
self.topology_accessor.ensure_is_routable().await
}
pub async fn ensure_contains_gateway(
&self,
gateway: &NodeIdentity,
) -> Result<(), NymTopologyError> {
let topology = self
.topology_accessor
.current_topology()
.await
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
if !topology.gateway_exists(gateway) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: gateway.to_base58_string(),
});
}
Ok(())
}
pub async fn wait_for_gateway(
&mut self,
gateway: &NodeIdentity,
timeout_duration: Duration,
) -> Result<(), NymTopologyError> {
info!(
"going to wait for at most {timeout_duration:?} for gateway '{gateway}' to come online"
);
let deadline = sleep(timeout_duration);
tokio::pin!(deadline);
loop {
tokio::select! {
_ = &mut deadline => {
return Err(NymTopologyError::TimedOutWaitingForGateway {
identity_key: gateway.to_base58_string()
})
}
_ = self.try_refresh() => {
if self.ensure_contains_gateway(gateway).await.is_ok() {
return Ok(())
}
info!("gateway '{gateway}' is still not online...");
sleep(self.refresh_rate).await
}
}
}
}
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
@@ -35,7 +35,7 @@ pub struct ClientKeysPaths {
}
impl ClientKeysPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
ClientKeysPaths {
@@ -29,14 +29,14 @@ pub struct CommonClientPaths {
}
impl CommonClientPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
CommonClientPaths {
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
gateway_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
keys: ClientKeysPaths::new_base(base_data_directory),
keys: ClientKeysPaths::new_default(base_data_directory),
}
}
}
+5 -52
View File
@@ -3,7 +3,6 @@
use nym_config::defaults::NymNetworkDetails;
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::GatewayConfig;
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
@@ -30,8 +29,6 @@ const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20)
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
@@ -140,17 +137,6 @@ impl Config {
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
}
pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_poisson_process()
}
self
}
pub fn set_no_poisson_process(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic()
@@ -244,19 +230,6 @@ pub struct GatewayEndpointConfig {
pub gateway_listener: String,
}
impl TryFrom<GatewayEndpointConfig> for GatewayConfig {
type Error = ClientCoreError;
fn try_from(value: GatewayEndpointConfig) -> Result<Self, Self::Error> {
Ok(GatewayConfig {
gateway_identity: identity::PublicKey::from_base58_string(value.gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?,
gateway_owner: Some(value.gateway_owner),
gateway_listener: value.gateway_listener,
})
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
impl GatewayEndpointConfig {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
@@ -279,29 +252,15 @@ impl GatewayEndpointConfig {
identity::PublicKey::from_base58_string(&self.gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
}
}
pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self {
// TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct
if use_tls {
Self::from_topology_node_tls(node)
} else {
Self::from_topology_node_no_tls(node)
}
}
pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self {
impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
fn from(node: nym_topology::gateway::Node) -> GatewayEndpointConfig {
let gateway_listener = node.clients_address();
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address(),
gateway_owner: node.owner,
}
}
pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address_tls(),
gateway_owner: node.owner,
gateway_listener,
}
}
}
@@ -528,11 +487,6 @@ pub struct Topology {
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructure,
}
@@ -567,7 +521,6 @@ impl Default for Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructure::default(),
}
}
@@ -267,7 +267,7 @@ impl From<TopologyV1_1_20_2> for Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
..Default::default()
topology_structure: Default::default(),
}
}
}
+16 -20
View File
@@ -1,7 +1,6 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::ErasedGatewayError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError;
use nym_topology::gateway::GatewayConversionError;
@@ -20,12 +19,6 @@ pub enum ClientCoreError {
source: GatewayClientError,
},
#[error("Custom gateway client error: {source}")]
ErasedGatewayClientError {
#[from]
source: ErasedGatewayError,
},
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
@@ -38,9 +31,15 @@ pub enum ClientCoreError {
#[error("No gateways on network")]
NoGatewaysOnNetwork,
#[error("Failed to setup gateway")]
FailedToSetupGateway,
#[error("List of nym apis is empty")]
ListOfNymApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
#[error("The current network topology seem to be insufficient to route any packets through")]
InsufficientNetworkTopology(#[from] NymTopologyError),
@@ -62,6 +61,15 @@ pub enum ClientCoreError {
#[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
#[error("The identity of the gateway is unknown - did you run init?")]
GatewayIdUnknown,
#[error("The owner of the gateway is unknown - did you run init?")]
GatewayOwnerUnknown,
#[error("The address of the gateway is unknown - did you run init?")]
GatewayAddressUnknown,
#[error("The gateway is malformed: {source}")]
MalformedGateway {
#[from]
@@ -114,18 +122,6 @@ pub enum ClientCoreError {
#[error("unable to upgrade config file to `{new_version}`")]
UnableToUpgradeConfigFile { new_version: String },
#[error("the provided gateway details don't much the stored data")]
MismatchedStoredGatewayDetails,
#[error("custom selection of gateway was expected")]
CustomGatewaySelectionExpected,
#[error("the persisted gateway details were set for a custom setup")]
UnexpectedPersistedCustomGatewayDetails,
#[error("this client has performed gateway initialisation in another session")]
NoInitClientPresent,
}
/// Set of messages that the client can send to listeners via the task manager
+10 -43
View File
@@ -3,12 +3,12 @@
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::types::RegistrationResult;
use crate::init::RegistrationResult;
use futures::{SinkExt, StreamExt};
use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient;
use nym_topology::{filter::VersionFilterable, gateway, mix};
use nym_topology::{filter::VersionFilterable, gateway};
use rand::{seq::SliceRandom, Rng};
use std::{sync::Arc, time::Duration};
use tungstenite::Message;
@@ -24,7 +24,6 @@ use tokio_tungstenite::connect_async;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
use nym_validator_client::client::IdentityKeyRef;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
@@ -78,28 +77,6 @@ pub async fn current_gateways<R: Rng>(
Ok(filtered_gateways)
}
pub async fn current_mixnodes<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
) -> Result<Vec<mix::Node>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of mixnodes from: {nym_api}");
let mixnodes = client.get_cached_mixnodes().await?;
let valid_mixnodes = mixnodes
.into_iter()
.filter_map(|mixnode| (&mixnode.bond_information).try_into().ok())
.collect::<Vec<mix::Node>>();
// we were always filtering by version so I'm not removing that 'feature'
let filtered_mixnodes = valid_mixnodes.filter_by_version(env!("CARGO_PKG_VERSION"));
Ok(filtered_mixnodes)
}
#[cfg(not(target_arch = "wasm32"))]
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
@@ -220,27 +197,17 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
.cloned()
}
pub(super) fn get_specified_gateway(
gateway_identity: IdentityKeyRef,
gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
}
pub(super) async fn register_with_gateway(
gateway: &GatewayEndpointConfig,
our_identity: Arc<identity::KeyPair>,
) -> Result<RegistrationResult, ClientCoreError> {
let mut gateway_client =
GatewayClient::new_init(gateway.to_owned().try_into()?, our_identity.clone());
let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init(
gateway.gateway_listener.clone(),
gateway.try_get_gateway_identity_key()?,
our_identity.clone(),
timeout,
);
gateway_client.establish_connection().await.map_err(|err| {
log::warn!("Failed to establish connection with gateway!");
ClientCoreError::GatewayClientError {
@@ -263,6 +230,6 @@ pub(super) async fn register_with_gateway(
})?;
Ok(RegistrationResult {
shared_keys,
authenticated_ephemeral_client: gateway_client,
authenticated_ephemeral_client: Some(gateway_client),
})
}
+372 -139
View File
@@ -8,34 +8,246 @@ use crate::client::base_client::storage::gateway_details::{
};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys;
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::helpers::{
choose_gateway_by_latency, get_specified_gateway, uniformly_random_gateway,
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
use crate::{
config::{Config, GatewayEndpointConfig},
error::ClientCoreError,
};
use crate::init::types::{
CustomGatewayDetails, GatewayDetails, GatewaySelectionSpecification, GatewaySetup,
InitialisationResult,
};
use nym_gateway_client::client::InitGatewayClient;
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::InitOnly;
use nym_gateway_client::GatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fmt::{Debug, Display};
use std::sync::Arc;
use url::Url;
pub mod helpers;
pub mod types;
pub struct RegistrationResult {
pub shared_keys: Arc<SharedKeys>,
pub authenticated_ephemeral_client: Option<GatewayClient<InitOnly>>,
}
pub struct InitialisationResult {
pub details: InitialisationDetails,
pub authenticated_ephemeral_client: Option<GatewayClient<InitOnly>>,
}
impl From<InitialisationDetails> for InitialisationResult {
fn from(details: InitialisationDetails) -> Self {
InitialisationResult {
details,
authenticated_ephemeral_client: None,
}
}
}
// TODO: rename to something better...
#[derive(Debug)]
pub struct InitialisationDetails {
pub gateway_details: GatewayEndpointConfig,
pub managed_keys: ManagedKeys,
}
impl InitialisationDetails {
pub fn new(gateway_details: GatewayEndpointConfig, managed_keys: ManagedKeys) -> Self {
InitialisationDetails {
gateway_details,
managed_keys,
}
}
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
if !loaded_details.verify(&loaded_keys.must_get_gateway_shared_key()) {
return Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: loaded_details.details.gateway_id,
});
}
Ok(InitialisationDetails {
gateway_details: loaded_details.into(),
managed_keys: loaded_keys,
})
}
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
let client_recipient = Recipient::new(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&self.gateway_details.gateway_id)?,
);
Ok(client_recipient)
}
}
pub enum GatewaySetup {
/// The gateway specification MUST BE loaded from the underlying storage.
MustLoad,
/// Specifies usage of a new, random, gateway.
New {
/// Should the new gateway be selected based on latency.
by_latency: bool,
},
Specified {
/// Identity key of the gateway we want to try to use.
gateway_identity: IdentityKey,
},
Predefined {
/// Full gateway configuration
details: PersistedGatewayDetails,
},
ReuseConnection {
/// The authenticated ephemeral client that was created during `init`
authenticated_ephemeral_client: GatewayClient<InitOnly>,
/// Details of this pre-initialised client
details: InitialisationDetails,
},
}
impl From<PersistedGatewayDetails> for GatewaySetup {
fn from(details: PersistedGatewayDetails) -> Self {
GatewaySetup::Predefined { details }
}
}
impl From<IdentityKey> for GatewaySetup {
fn from(gateway_identity: IdentityKey) -> Self {
GatewaySetup::Specified { gateway_identity }
}
}
impl Default for GatewaySetup {
fn default() -> Self {
GatewaySetup::New { by_latency: false }
}
}
impl GatewaySetup {
pub fn new_fresh(
gateway_identity: Option<String>,
latency_based_selection: Option<bool>,
) -> Self {
if let Some(gateway_identity) = gateway_identity {
GatewaySetup::Specified { gateway_identity }
} else {
GatewaySetup::New {
by_latency: latency_based_selection.unwrap_or_default(),
}
}
}
pub fn is_must_load(&self) -> bool {
matches!(self, GatewaySetup::MustLoad)
}
pub fn has_full_details(&self) -> bool {
matches!(self, GatewaySetup::Predefined { .. }) || self.is_must_load()
}
pub async fn choose_gateway(
&self,
gateways: &[gateway::Node],
) -> Result<GatewayEndpointConfig, ClientCoreError> {
match self {
GatewaySetup::New { by_latency } => {
let mut rng = OsRng;
if *by_latency {
choose_gateway_by_latency(&mut rng, gateways).await
} else {
uniformly_random_gateway(&mut rng, gateways)
}
}
.map(Into::into),
GatewaySetup::Specified { gateway_identity } => {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
}
.map(Into::into),
_ => Err(ClientCoreError::UnexpectedGatewayDetails),
}
}
pub async fn try_get_new_gateway_details(
&self,
validator_servers: &[Url],
) -> Result<GatewayEndpointConfig, ClientCoreError> {
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?;
self.choose_gateway(&gateways).await
}
}
/// Struct describing the results of the client initialization procedure.
#[derive(Debug, Serialize)]
pub struct InitResults {
version: String,
id: String,
identity_key: String,
encryption_key: String,
gateway_id: String,
gateway_listener: String,
}
impl InitResults {
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
version: config.client.version.clone(),
id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: gateway.gateway_id.clone(),
gateway_listener: gateway.gateway_listener.clone(),
}
}
}
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Version: {}", self.version)?;
writeln!(f, "ID: {}", self.id)?;
writeln!(f, "Identity key: {}", self.identity_key)?;
writeln!(f, "Encryption: {}", self.encryption_key)?;
writeln!(f, "Gateway ID: {}", self.gateway_id)?;
write!(f, "Gateway: {}", self.gateway_listener)
}
}
// helpers for error wrapping
async fn _store_gateway_details<T, D>(
async fn _store_gateway_details<D>(
details_store: &D,
details: &PersistedGatewayDetails<T>,
details: &PersistedGatewayDetails,
) -> Result<(), ClientCoreError>
where
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
D::StorageError: Send + Sync + 'static,
T: Serialize + Send + Sync,
{
details_store
.store_gateway_details(details)
@@ -45,13 +257,12 @@ where
})
}
async fn _load_gateway_details<T, D>(
async fn _load_gateway_details<D>(
details_store: &D,
) -> Result<PersistedGatewayDetails<T>, ClientCoreError>
) -> Result<PersistedGatewayDetails, ClientCoreError>
where
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
details_store
.load_gateway_details()
@@ -73,168 +284,190 @@ where
})
}
fn ensure_valid_details<T>(
details: &PersistedGatewayDetails<T>,
fn ensure_valid_details(
details: &PersistedGatewayDetails,
loaded_keys: &ManagedKeys,
) -> Result<(), ClientCoreError> {
details.validate(loaded_keys.gateway_shared_key().as_deref())
if !details.verify(&loaded_keys.must_get_gateway_shared_key()) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
async fn setup_new_gateway<T, K, D>(
pub async fn setup_gateway_from<K, D>(
setup: GatewaySetup,
key_store: &K,
details_store: &D,
overwrite_data: bool,
selection_specification: GatewaySelectionSpecification<T>,
available_gateways: Vec<gateway::Node>,
) -> Result<InitialisationResult<T>, ClientCoreError>
gateways: Option<&[gateway::Node]>,
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
// if we're setting up new gateway, failing to load existing information is fine.
// as a matter of fact, it's only potentially a problem if we DO succeed
if _load_gateway_details(details_store).await.is_ok() && !overwrite_data {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
if _load_managed_keys(key_store).await.is_ok() && !overwrite_data {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
// I don't like how we can't deal with this variant in the match below, but we need to take ownership of internal values.
if let GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
details,
} = setup
{
// if we have already performed the full setup, forward the details.
// it's up to the caller to ensure persistence
return Ok(InitialisationResult {
details,
authenticated_ephemeral_client: Some(authenticated_ephemeral_client),
});
}
let mut rng = OsRng;
let mut new_keys = ManagedKeys::generate_new(&mut rng);
let gateway_details = match selection_specification {
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
// try load gateway details
let loaded_details = _load_gateway_details(details_store).await;
// try load keys and decide what to do based on the GatewaySetup
let mut managed_keys = match ManagedKeys::try_load(key_store).await {
Ok(loaded_keys) => {
match &setup {
GatewaySetup::MustLoad => {
// get EVERYTHING from the storage
let details = loaded_details?;
ensure_valid_details(&details, &loaded_keys)?;
// no need to persist anything as we got everything from the storage
return Ok(InitialisationDetails::new(details.into(), loaded_keys).into());
}
GatewaySetup::Predefined { details } => {
// we already have defined gateway details AND a shared key
ensure_valid_details(details, &loaded_keys)?;
// if nothing was stored or we're allowed to overwrite what's there, just persist the passed data
if overwrite_data || loaded_details.is_err() {
_store_gateway_details(details_store, details).await?;
}
return Ok(
InitialisationDetails::new(details.clone().into(), loaded_keys).into(),
);
}
GatewaySetup::Specified { gateway_identity } => {
// if that data was already stored...
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
if &existing_gateway.details.gateway_id != gateway_identity
&& !overwrite_data
{
// if our loaded details don't match requested value and we CANT overwrite it...
return Err(ClientCoreError::UnexpectedGatewayDetails);
} else if &existing_gateway.details.gateway_id == gateway_identity {
// if they do match up, just return it
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
)
.into());
}
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
GatewaySetup::New { .. } => {
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
)
.into());
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
GatewaySetup::ReuseConnection { .. } => {
unreachable!("the reuse connection variant was already manually covered")
}
}
}
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
Err(_) => {
// if we failed to load the keys, ensure we didn't provide gateway details in some form
// (in that case we CAN'T generate new keys
if setup.has_full_details() {
return Err(ClientCoreError::UnavailableSharedKey);
}
ManagedKeys::generate_new(&mut rng)
}
GatewaySelectionSpecification::Specified {
must_use_tls,
identity,
} => {
let gateway = get_specified_gateway(&identity, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
}
GatewaySelectionSpecification::Custom {
gateway_identity,
additional_data,
} => GatewayDetails::Custom(CustomGatewayDetails::new(gateway_identity, additional_data)),
};
let registration_result = if let GatewayDetails::Configured(gateway_cfg) = &gateway_details {
// if we're using a 'normal' gateway setup, do register
let our_identity = new_keys.identity_keypair();
Some(helpers::register_with_gateway(gateway_cfg, our_identity).await?)
} else {
None
};
// choose gateway
let gateway_details = setup.choose_gateway(gateways.unwrap_or_default()).await?;
let maybe_shared_keys = registration_result
.as_ref()
.map(|r| Arc::clone(&r.shared_keys));
// get our identity key
let our_identity = managed_keys.identity_keypair();
let persisted_details =
PersistedGatewayDetails::new(gateway_details, maybe_shared_keys.as_deref())?;
// Establish connection, authenticate and generate keys for talking with the gateway
let registration_result =
helpers::register_with_gateway(&gateway_details, our_identity).await?;
let shared_keys = registration_result.shared_keys;
// persist the keys
new_keys
.deal_with_gateway_key(maybe_shared_keys, key_store)
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
// persist gateway keys
managed_keys
.deal_with_gateway_key(shared_keys, key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
// persist gateway configs
// persist gateway config
_store_gateway_details(details_store, &persisted_details).await?;
Ok(InitialisationResult {
gateway_details: persisted_details.into(),
managed_keys: new_keys,
authenticated_ephemeral_client: registration_result
.map(|r| r.authenticated_ephemeral_client),
details: InitialisationDetails::new(persisted_details.into(), managed_keys),
authenticated_ephemeral_client: registration_result.authenticated_ephemeral_client,
})
}
async fn use_loaded_gateway_details<T, K, D>(
pub async fn setup_gateway<K, D>(
setup: GatewaySetup,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
overwrite_data: bool,
validator_servers: Option<&[Url]>,
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers.unwrap_or_default()).await?;
ensure_valid_details(&loaded_details, &loaded_keys)?;
// no need to persist anything as we got everything from the storage
Ok(InitialisationResult::new_loaded(
loaded_details.into(),
loaded_keys,
))
}
fn reuse_gateway_connection<T>(
authenticated_ephemeral_client: InitGatewayClient,
gateway_details: GatewayDetails<T>,
managed_keys: ManagedKeys,
) -> InitialisationResult<T> {
InitialisationResult {
gateway_details,
managed_keys,
authenticated_ephemeral_client: Some(authenticated_ephemeral_client),
}
}
pub async fn setup_gateway<T, K, D>(
setup: GatewaySetup<T>,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
match setup {
GatewaySetup::MustLoad => use_loaded_gateway_details(key_store, details_store).await,
GatewaySetup::New {
specification,
available_gateways,
overwrite_data,
} => {
setup_new_gateway(
key_store,
details_store,
overwrite_data,
specification,
available_gateways,
)
.await
}
GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details,
managed_keys,
} => Ok(reuse_gateway_connection(
authenticated_ephemeral_client,
gateway_details,
managed_keys,
)),
}
setup_gateway_from(
setup,
key_store,
details_store,
overwrite_data,
Some(&gateways),
)
.await
}
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
-329
View File
@@ -1,329 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedCustomGatewayDetails, PersistedGatewayDetails,
};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys;
use crate::config::{Config, GatewayEndpointConfig};
use crate::error::ClientCoreError;
use crate::init::{_load_gateway_details, _load_managed_keys, setup_gateway};
use nym_gateway_client::client::InitGatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::sync::Arc;
/// Result of registering with a gateway:
/// - shared keys derived between ourselves and the node
/// - an authenticated handle of an ephemeral handle created for the purposes of registration
pub struct RegistrationResult {
pub shared_keys: Arc<SharedKeys>,
pub authenticated_ephemeral_client: InitGatewayClient,
}
/// Result of fully initialising a client:
/// - details of the associated gateway
/// - all loaded (or derived) keys
/// - an optional authenticated handle of an ephemeral gateway handle created for the purposes of registration,
/// if this was the first time this client registered
pub struct InitialisationResult<T = EmptyCustomDetails> {
pub gateway_details: GatewayDetails<T>,
pub managed_keys: ManagedKeys,
pub authenticated_ephemeral_client: Option<InitGatewayClient>,
}
impl<T> InitialisationResult<T> {
pub fn new_loaded(gateway_details: GatewayDetails<T>, managed_keys: ManagedKeys) -> Self {
InitialisationResult {
gateway_details,
managed_keys,
authenticated_ephemeral_client: None,
}
}
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
match &loaded_details {
PersistedGatewayDetails::Default(loaded_default) => {
if !loaded_default.verify(&loaded_keys.must_get_gateway_shared_key()) {
return Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: loaded_default.details.gateway_id.clone(),
});
}
}
PersistedGatewayDetails::Custom(_) => {}
}
Ok(InitialisationResult {
gateway_details: loaded_details.into(),
managed_keys: loaded_keys,
authenticated_ephemeral_client: None,
})
}
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
let client_recipient = Recipient::new(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(self.gateway_details.gateway_id())?,
);
Ok(client_recipient)
}
}
/// Details of particular gateway client got registered with
#[derive(Debug, Clone)]
pub enum GatewayDetails<T = EmptyCustomDetails> {
/// Standard details of a remote gateway
Configured(GatewayEndpointConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(CustomGatewayDetails<T>),
}
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct EmptyCustomDetails {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomGatewayDetails<T = EmptyCustomDetails> {
// whatever custom method is used, gateway's identity must be known
pub gateway_id: String,
#[serde(flatten)]
pub additional_data: T,
}
impl<T> CustomGatewayDetails<T> {
pub fn new(gateway_id: String, additional_data: T) -> Self {
Self {
gateway_id,
additional_data,
}
}
}
impl<T> GatewayDetails<T> {
pub fn try_get_configured_endpoint(&self) -> Option<&GatewayEndpointConfig> {
if let GatewayDetails::Configured(endpoint) = &self {
Some(endpoint)
} else {
None
}
}
pub fn is_custom(&self) -> bool {
matches!(self, GatewayDetails::Custom(_))
}
pub fn gateway_id(&self) -> &str {
match self {
GatewayDetails::Configured(cfg) => &cfg.gateway_id,
GatewayDetails::Custom(custom) => &custom.gateway_id,
}
}
}
impl From<GatewayEndpointConfig> for GatewayDetails {
fn from(value: GatewayEndpointConfig) -> Self {
GatewayDetails::Configured(value)
}
}
impl<T> From<PersistedCustomGatewayDetails<T>> for CustomGatewayDetails<T> {
fn from(value: PersistedCustomGatewayDetails<T>) -> Self {
CustomGatewayDetails {
gateway_id: value.gateway_id,
additional_data: value.additional_data,
}
}
}
impl<T> From<CustomGatewayDetails<T>> for PersistedCustomGatewayDetails<T> {
fn from(value: CustomGatewayDetails<T>) -> Self {
PersistedCustomGatewayDetails {
gateway_id: value.gateway_id,
additional_data: value.additional_data,
}
}
}
impl<T> From<PersistedGatewayDetails<T>> for GatewayDetails<T> {
fn from(value: PersistedGatewayDetails<T>) -> Self {
match value {
PersistedGatewayDetails::Default(default) => {
GatewayDetails::Configured(default.details)
}
PersistedGatewayDetails::Custom(custom) => GatewayDetails::Custom(custom.into()),
}
}
}
#[derive(Clone)]
pub enum GatewaySelectionSpecification<T = EmptyCustomDetails> {
/// Uniformly choose a random remote gateway.
UniformRemote { must_use_tls: bool },
/// Should the new, remote, gateway be selected based on latency.
RemoteByLatency { must_use_tls: bool },
/// Gateway with this specific identity should be chosen.
// JS: I don't really like the name of this enum variant but couldn't think of anything better at the time
Specified {
must_use_tls: bool,
identity: IdentityKey,
},
// TODO: this doesn't really fit in here..., but where else to put it?
/// This client has handled the selection by itself
Custom {
gateway_identity: String,
additional_data: T,
},
}
impl<T> Default for GatewaySelectionSpecification<T> {
fn default() -> Self {
GatewaySelectionSpecification::UniformRemote {
must_use_tls: false,
}
}
}
impl<T> GatewaySelectionSpecification<T> {
pub fn new(
gateway_identity: Option<String>,
latency_based_selection: Option<bool>,
must_use_tls: bool,
) -> Self {
if let Some(identity) = gateway_identity {
GatewaySelectionSpecification::Specified {
identity,
must_use_tls,
}
} else if let Some(true) = latency_based_selection {
GatewaySelectionSpecification::RemoteByLatency { must_use_tls }
} else {
GatewaySelectionSpecification::UniformRemote { must_use_tls }
}
}
}
pub enum GatewaySetup<T = EmptyCustomDetails> {
/// The gateway specification (details + keys) MUST BE loaded from the underlying storage.
MustLoad,
/// Specifies usage of a new gateway
New {
specification: GatewaySelectionSpecification<T>,
// TODO: seems to be a bit inefficient to pass them by value
available_gateways: Vec<gateway::Node>,
/// Specifies whether old data should be overwritten whilst setting up new gateway client.
overwrite_data: bool,
},
ReuseConnection {
/// The authenticated ephemeral client that was created during `init`
authenticated_ephemeral_client: InitGatewayClient,
// Details of this pre-initialised client (i.e. gateway and keys)
gateway_details: GatewayDetails<T>,
managed_keys: ManagedKeys,
},
}
impl<T> GatewaySetup<T> {
pub fn try_reuse_connection(
init_res: InitialisationResult<T>,
) -> Result<Self, ClientCoreError> {
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
Ok(GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details: init_res.gateway_details,
managed_keys: init_res.managed_keys,
})
} else {
Err(ClientCoreError::NoInitClientPresent)
}
}
pub async fn try_setup<K, D>(
self,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
setup_gateway(self, key_store, details_store).await
}
pub fn is_must_load(&self) -> bool {
matches!(self, GatewaySetup::MustLoad)
}
pub fn has_full_details(&self) -> bool {
self.is_must_load()
}
}
/// Struct describing the results of the client initialization procedure.
#[derive(Debug, Serialize)]
pub struct InitResults {
version: String,
id: String,
identity_key: String,
encryption_key: String,
gateway_id: String,
gateway_listener: String,
}
impl InitResults {
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
version: config.client.version.clone(),
id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: gateway.gateway_id.clone(),
gateway_listener: gateway.gateway_listener.clone(),
}
}
}
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Version: {}", self.version)?;
writeln!(f, "ID: {}", self.id)?;
writeln!(f, "Identity key: {}", self.identity_key)?;
writeln!(f, "Encryption: {}", self.encryption_key)?;
writeln!(f, "Gateway ID: {}", self.gateway_id)?;
write!(f, "Gateway: {}", self.gateway_listener)
}
}
-5
View File
@@ -5,11 +5,6 @@ pub mod config;
pub mod error;
pub mod init;
pub use nym_topology::{
HardcodedTopologyProvider, NymTopology, NymTopologyError, SerializableNymTopology,
SerializableTopologyError, TopologyProvider,
};
#[cfg(target_arch = "wasm32")]
pub(crate) fn spawn_future<F>(future: F)
where
+2 -2
View File
@@ -31,7 +31,7 @@ serde = { workspace = true, features = ["derive"] }
[dependencies.tungstenite]
workspace = true
version = "0.13"
default-features = false
# non-wasm-only dependencies
@@ -44,7 +44,7 @@ version = "0.1.11"
features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
workspace = true
version = "0.14"
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
+33 -52
View File
@@ -7,7 +7,6 @@ pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
};
use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt};
use log::*;
@@ -40,23 +39,9 @@ use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep;
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
pub struct GatewayConfig {
pub gateway_identity: identity::PublicKey,
// currently a dead field
pub gateway_owner: Option<String>,
pub gateway_listener: String,
}
// TODO: this should be refactored into a state machine that keeps track of its authentication state
pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
authenticated: bool,
disabled_credentials_mode: bool,
@@ -84,12 +69,17 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
}
impl<C, St> GatewayClient<C, St> {
// TODO: put it all in a Config struct
#[allow(clippy::too_many_arguments)]
pub fn new(
config: GatewayConfig,
gateway_address: String,
local_identity: Arc<identity::KeyPair>,
gateway_identity: identity::PublicKey,
// TODO: make it mandatory. if you don't want to pass it, use `new_init`
shared_key: Option<Arc<SharedKeys>>,
packet_router: PacketRouter,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, St>>,
shutdown: TaskClient,
) -> Self {
@@ -97,13 +87,13 @@ impl<C, St> GatewayClient<C, St> {
authenticated: false,
disabled_credentials_mode: true,
bandwidth_remaining: 0,
gateway_address: config.gateway_listener,
gateway_identity: config.gateway_identity,
gateway_address,
gateway_identity,
local_identity,
shared_key,
connection: SocketState::NotConnected,
packet_router,
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
response_timeout_duration,
bandwidth_controller,
should_reconnect_on_failure: true,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
@@ -112,34 +102,21 @@ impl<C, St> GatewayClient<C, St> {
}
}
#[must_use]
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) {
self.disabled_credentials_mode = disabled_credentials_mode;
self
}
#[must_use]
pub fn with_reconnection_on_failure(mut self, should_reconnect_on_failure: bool) -> Self {
self.should_reconnect_on_failure = should_reconnect_on_failure;
self
// TODO: later convert into proper builder methods
pub fn with_reconnection_on_failure(&mut self, should_reconnect_on_failure: bool) {
self.should_reconnect_on_failure = should_reconnect_on_failure
}
#[must_use]
pub fn with_response_timeout(mut self, response_timeout_duration: Duration) -> Self {
self.response_timeout_duration = response_timeout_duration;
self
pub fn with_reconnection_attempts(&mut self, reconnection_attempts: usize) {
self.reconnection_attempts = reconnection_attempts
}
#[must_use]
pub fn with_reconnection_attempts(mut self, reconnection_attempts: usize) -> Self {
self.reconnection_attempts = reconnection_attempts;
self
}
#[must_use]
pub fn with_reconnection_backoff(mut self, backoff: Duration) -> Self {
self.reconnection_backoff = backoff;
self
pub fn with_reconnection_backoff(&mut self, backoff: Duration) {
self.reconnection_backoff = backoff
}
pub fn gateway_identity(&self) -> identity::PublicKey {
@@ -784,14 +761,16 @@ impl<C, St> GatewayClient<C, St> {
}
}
// type alias for an ease of use
pub type InitGatewayClient = GatewayClient<InitOnly>;
pub struct InitOnly;
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
// for initialisation we do not need credential storage. Though it's still a bit weird we have to set the generic...
pub fn new_init(config: GatewayConfig, local_identity: Arc<identity::KeyPair>) -> Self {
pub fn new_init(
gateway_address: String,
gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>,
response_timeout_duration: Duration,
) -> Self {
use futures::channel::mpsc;
// note: this packet_router is completely invalid in normal circumstances, but "works"
@@ -805,13 +784,13 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
authenticated: false,
disabled_credentials_mode: true,
bandwidth_remaining: 0,
gateway_address: config.gateway_listener,
gateway_identity: config.gateway_identity,
gateway_address,
gateway_identity,
local_identity,
shared_key: None,
connection: SocketState::NotConnected,
packet_router,
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
response_timeout_duration,
bandwidth_controller: None,
should_reconnect_on_failure: false,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
@@ -822,7 +801,9 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
pub fn upgrade<C, St>(
self,
packet_router: PacketRouter,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, St>>,
shutdown: TaskClient,
) -> GatewayClient<C, St> {
@@ -841,8 +822,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
local_identity: self.local_identity,
shared_key: self.shared_key,
connection: self.connection,
packet_router,
response_timeout_duration: self.response_timeout_duration,
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
response_timeout_duration,
bandwidth_controller,
should_reconnect_on_failure: self.should_reconnect_on_failure,
reconnection_attempts: self.reconnection_attempts,
+6 -16
View File
@@ -13,17 +13,17 @@ pub enum GatewayClientError {
#[error("Connection to the gateway is not established")]
ConnectionNotEstablished,
#[error("Gateway returned an error response: {0}")]
#[error("Gateway returned an error response - {0}")]
GatewayError(String),
#[error("There was a network error: {0}")]
#[error("There was a network error - {0}")]
NetworkError(#[from] WsError),
#[cfg(target_arch = "wasm32")]
#[error("There was a network error: {0}")]
#[error("There was a network error")]
NetworkErrorWasm(#[from] JsError),
#[error("Invalid URL: {0}")]
#[error("Invalid URL - {0}")]
InvalidURL(String),
#[error("No shared key was provided or obtained")]
@@ -32,7 +32,7 @@ pub enum GatewayClientError {
#[error("No bandwidth controller provided")]
NoBandwidthControllerAvailable,
#[error("Bandwidth controller error: {0}")]
#[error("Bandwidth controller error - {0}")]
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
#[error("Connection was abruptly closed")]
@@ -62,7 +62,7 @@ pub enum GatewayClientError {
#[error("Connection is in an invalid state - please send a bug report")]
ConnectionInInvalidState,
#[error("Failed to finish registration handshake: {0}")]
#[error("Failed to finish registration handshake - {0}")]
RegistrationFailure(HandshakeError),
#[error("Authentication failure")]
@@ -76,16 +76,6 @@ pub enum GatewayClientError {
#[error("Attempted to negotiate connection with gateway using incompatible protocol version. Ours is {current} and the gateway reports {gateway:?}")]
IncompatibleProtocol { gateway: Option<u8>, current: u8 },
#[error(
"The packet router hasn't been set - are you sure you started up the client correctly?"
)]
PacketRouterUnavailable,
#[error(
"this operation couldn't be completed as the program is in the process of shutting down"
)]
ShutdownInProgress,
}
impl GatewayClientError {
+4 -7
View File
@@ -2,23 +2,20 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::GatewayClientError;
pub use client::GatewayClient;
use log::warn;
use nym_gateway_requests::BinaryResponse;
use tungstenite::{protocol::Message, Error as WsError};
pub use client::{GatewayClient, GatewayConfig};
pub use nym_gateway_requests::registration::handshake::SharedKeys;
pub use packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
PacketRouter,
};
pub use traits::GatewayPacketRouter;
use tungstenite::{protocol::Message, Error as WsError};
pub use nym_gateway_requests::registration::handshake::SharedKeys;
pub mod client;
pub mod error;
pub mod packet_router;
pub mod socket_state;
pub mod traits;
/// Helper method for reading from websocket stream. Helps to flatten the structure.
pub(crate) fn cleanup_socket_message(
@@ -5,8 +5,10 @@
// I will gladly take any suggestions on how to rename this.
use crate::error::GatewayClientError;
use crate::GatewayPacketRouter;
use futures::channel::mpsc;
use log::*;
use nym_sphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx::params::packet_sizes::PacketSize;
use nym_task::TaskClient;
pub type MixnetMessageSender = mpsc::UnboundedSender<Vec<Vec<u8>>>;
@@ -35,52 +37,77 @@ impl PacketRouter {
}
}
pub fn route_mixnet_messages(
&self,
received_messages: Vec<Vec<u8>>,
pub fn route_received(
&mut self,
unwrapped_packets: Vec<Vec<u8>>,
) -> Result<(), GatewayClientError> {
if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) {
// check if the failure is due to the shutdown being in progress and thus the receiver channel
// having already been dropped
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send mixnet messages due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress);
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send mixnet message: {err}");
}
Ok(())
}
let mut received_messages = Vec::new();
let mut received_acks = Vec::new();
pub fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), GatewayClientError> {
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
// check if the failure is due to the shutdown being in progress and thus the receiver channel
// having already been dropped
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send acks due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress);
// remember: gateway removes final layer of sphinx encryption and from the unwrapped
// data he takes the SURB-ACK and first hop address.
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let outfox_ack_overhead =
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
for received_packet in unwrapped_packets {
if received_packet.len() == PacketSize::AckPacket.plaintext_size()
// we don't know the real size of the payload, it could be anything <= 48 bytes
|| received_packet.len() <= PacketSize::OutfoxAckPacket.plaintext_size()
{
received_acks.push(received_packet);
} else if received_packet.len()
== PacketSize::RegularPacket.plaintext_size() - ack_overhead
|| received_packet.len()
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead
|| received_packet.len()
== PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead
{
trace!("routing regular packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket8.plaintext_size() - ack_overhead
{
trace!("routing extended8 packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket16.plaintext_size() - ack_overhead
{
trace!("routing extended16 packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket32.plaintext_size() - ack_overhead
{
trace!("routing extended32 packet");
received_messages.push(received_packet);
} else {
// this can happen if other clients are not padding their messages
warn!("Received message of unexpected size. Probably from an outdated client... len: {}", received_packet.len());
received_messages.push(received_packet);
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send acks: {err}");
}
if !received_messages.is_empty() {
trace!("routing 'real'");
if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) {
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send mixnet message due to receiver task shutdown");
return Err(GatewayClientError::MixnetMsgSenderFailedToSend);
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send mixnet message: {err}");
}
}
if !received_acks.is_empty() {
trace!("routing acks");
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
error!("failed to send ack: {err}");
};
}
Ok(())
}
}
impl GatewayPacketRouter for PacketRouter {
type Error = GatewayClientError;
// note: this trait tries to decide whether a given message is an ack or a data message
fn route_mixnet_messages(&self, received_messages: Vec<Vec<u8>>) -> Result<(), Self::Error> {
self.route_mixnet_messages(received_messages)
}
fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), Self::Error> {
self.route_acks(received_acks)
}
}
@@ -3,7 +3,6 @@
use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter;
use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_messages, try_decrypt_binary_message};
use futures::channel::oneshot;
use futures::stream::{SplitSink, SplitStream};
@@ -1,96 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use log::{error, trace, warn};
use nym_sphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx::params::PacketSize;
pub trait GatewayPacketRouter {
type Error: std::error::Error;
fn route_received(&self, unwrapped_packets: Vec<Vec<u8>>) -> Result<(), Self::Error> {
let mut received_messages = Vec::new();
let mut received_acks = Vec::new();
// remember: gateway removes final layer of sphinx encryption and from the unwrapped
// data he takes the SURB-ACK and first hop address.
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let outfox_ack_overhead =
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
for received_packet in unwrapped_packets {
// note: if we ever fail to route regular outfox, it might be because I've removed a match on
// `size == PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead` since it seemed
// redundant given we have `size == PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead`
// and all the headers should have already be stripped at this point
match received_packet.len() {
n if n == PacketSize::AckPacket.plaintext_size() => {
trace!("received sphinx ack");
received_acks.push(received_packet);
}
n if n <= PacketSize::OutfoxAckPacket.plaintext_size() => {
// we don't know the real size of the payload, it could be anything <= 48 bytes
trace!("received outfox ack");
received_acks.push(received_packet);
}
n if n == PacketSize::RegularPacket.plaintext_size() - sphinx_ack_overhead => {
trace!("received regular sphinx packet");
received_messages.push(received_packet);
}
n if n
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead =>
{
trace!("received regular outfox packet");
received_messages.push(received_packet);
}
n if n == PacketSize::ExtendedPacket8.plaintext_size() - sphinx_ack_overhead => {
trace!("received extended8 packet");
received_messages.push(received_packet);
}
n if n == PacketSize::ExtendedPacket16.plaintext_size() - sphinx_ack_overhead => {
trace!("received extended16 packet");
received_messages.push(received_packet);
}
n if n == PacketSize::ExtendedPacket32.plaintext_size() - sphinx_ack_overhead => {
trace!("received extended32 packet");
received_messages.push(received_packet);
}
n => {
// this can happen if other clients are not padding their messages
warn!("Received message of unexpected size. Probably from an outdated client... len: {n}");
received_messages.push(received_packet);
}
}
}
if !received_messages.is_empty() {
trace!("routing {} received packets", received_messages.len());
if let Err(err) = self.route_mixnet_messages(received_messages) {
error!("failed to route received messages: {err}");
return Err(err);
}
}
if !received_acks.is_empty() {
trace!("routing {} received acks", received_acks.len());
if let Err(err) = self.route_acks(received_acks) {
error!("failed to route received acks: {err}");
return Err(err);
}
}
Ok(())
}
fn route_mixnet_messages(&self, received_messages: Vec<Vec<u8>>) -> Result<(), Self::Error>;
fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), Self::Error>;
}
@@ -56,7 +56,7 @@ impl PacketForwarder {
log::trace!("PacketForwarder: Received shutdown");
}
Some(mix_packet) = self.packet_receiver.next() => {
trace!("Going to forward packet to {}", mix_packet.next_hop());
trace!("Going to forward packet to {:?}", mix_packet.next_hop());
let next_hop = mix_packet.next_hop();
let packet_type = mix_packet.packet_type();
@@ -86,5 +86,4 @@ required-features = ["http-client"]
default = ["http-client"]
http-client = ["cosmrs/rpc", "openssl"]
generate-ts = []
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
@@ -683,14 +683,13 @@ pub trait MixnetSigningClient {
.await
}
#[cfg(feature = "contract-testing")]
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
async fn testing_resolve_all_pending_events(
&self,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::TestingResolveAllPendingEvents { limit: None },
MixnetExecuteMsg::TestingResolveAllPendingEvents {},
vec![],
)
.await
@@ -929,8 +928,8 @@ mod tests {
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
.ignore(),
#[cfg(feature = "contract-testing")]
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
MixnetExecuteMsg::TestingResolveAllPendingEvents {} => {
client.testing_resolve_all_pending_events(None).ignore()
}
};
+6 -5
View File
@@ -33,17 +33,18 @@ pub fn may_get_home() -> Option<PathBuf> {
}
pub trait NymConfigTemplate: Serialize {
fn template(&self) -> &'static str;
fn template() -> &'static str;
fn format_to_string(&self) -> String {
// it is responsibility of whoever is implementing the trait to ensure the template is valid
Handlebars::new()
.render_template(self.template(), &self)
.render_template(Self::template(), &self)
.unwrap()
}
fn format_to_writer<W: Write>(&self, writer: W) -> io::Result<()> {
if let Err(err) = Handlebars::new().render_template_to_write(self.template(), &self, writer)
if let Err(err) =
Handlebars::new().render_template_to_write(Self::template(), &self, writer)
{
match err {
TemplateRenderError::IOError(err, _) => return Err(err),
@@ -63,7 +64,7 @@ where
C: NymConfigTemplate,
P: AsRef<Path>,
{
log::debug!("trying to save config file to {}", path.as_ref().display());
log::trace!("trying to save config file to {}", path.as_ref().display());
let file = File::create(path.as_ref())?;
// TODO: check for whether any of our configs stores anything sensitive
@@ -107,7 +108,7 @@ where
//
//
// pub trait NymConfig: Default + Serialize + DeserializeOwned {
// fn template(&self) -> &'static str;
// fn template() -> &'static str;
//
// fn config_file_name() -> String {
// "config.toml".to_string()
@@ -572,7 +572,7 @@ mod tests {
let env = mock_env();
// epoch just begun
let mut interval = Interval {
let interval = Interval {
id: 0,
epochs_in_interval: 100,
current_epoch_start: OffsetDateTime::from_unix_timestamp(
@@ -586,16 +586,19 @@ mod tests {
assert!(!interval.is_current_epoch_over(&env));
// current time == current epoch start
let mut interval = interval;
interval.current_epoch_start =
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap();
assert!(!interval.is_current_epoch_over(&env));
// epoch HASN'T yet begun (weird edge case, but can happen if we decide to manually adjust things)
let mut interval = interval;
interval.current_epoch_start =
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64 + 100).unwrap();
assert!(!interval.is_current_epoch_over(&env));
// current_time = EXACTLY end of the epoch
let mut interval = interval;
interval.current_epoch_start =
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap()
- interval.epoch_length;
+1 -1
View File
@@ -71,7 +71,7 @@ pub async fn setup_recovery_storage(recovery_dir: PathBuf) -> RecoveryStorage {
pub async fn setup_persistent_storage(client_home_directory: PathBuf) -> PersistentStorage {
let data_dir = client_home_directory.join(DEFAULT_DATA_DIR);
let paths = CommonClientPaths::new_base(data_dir);
let paths = CommonClientPaths::new_default(data_dir);
let db_path = paths.credentials_database;
nym_credential_storage::initialise_persistent_storage(db_path).await
+2 -8
View File
@@ -150,13 +150,6 @@ impl PublicKey {
}
}
#[cfg(feature = "sphinx")]
impl From<PublicKey> for DestinationAddressBytes {
fn from(value: PublicKey) -> Self {
value.derive_destination_address()
}
}
impl FromStr for PublicKey {
type Err = Ed25519RecoveryError;
@@ -249,7 +242,8 @@ impl PrivateKey {
/// Signs text with the provided Ed25519 private key, returning a base58 signature
pub fn sign_text(&self, text: &str) -> String {
let signature_bytes = self.sign(text.as_ref()).to_bytes();
bs58::encode(signature_bytes).into_string()
let signature = bs58::encode(signature_bytes).into_string();
signature
}
}
+9 -8
View File
@@ -4,9 +4,9 @@
use crate::var_names;
use crate::{DenomDetails, ValidatorDetails};
pub const NETWORK_NAME: &str = "mainnet";
pub(crate) const NETWORK_NAME: &str = "mainnet";
pub const BECH32_PREFIX: &str = "n";
pub(crate) const BECH32_PREFIX: &str = "n";
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
@@ -15,12 +15,13 @@ pub const MIXNET_CONTRACT_ADDRESS: &str =
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
pub const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const EPHEMERA_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const EPHEMERA_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
+1 -1
View File
@@ -80,7 +80,7 @@ pub fn number_of_required_fragments(
let max_linked = linked_fragment_payload_max_len(plaintext_per_fragment);
match set::total_number_of_sets(message_len, plaintext_per_fragment) {
1 => {
n if n == 1 => {
// is if it's a single fragment message
if message_len < max_unlinked {
return (1, max_unlinked - message_len);
+1 -1
View File
@@ -89,7 +89,7 @@ pub enum PacketSize {
impl PartialOrd for PacketSize {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// order them by actual packet size
Some(self.cmp(other))
self.size().partial_cmp(&other.size())
}
}
-1
View File
@@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { workspace = true }
dirs = "4.0"
futures = { workspace = true }
log = { workspace = true }

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