Compare commits

..

1 Commits

Author SHA1 Message Date
Tommy Verrall a2c473e9d3 Merge branch 'develop' into conflicts 2024-07-10 11:43:54 +02:00
980 changed files with 22626 additions and 51024 deletions
-7
View File
@@ -1,7 +0,0 @@
.git
.github
.gitignore
**/node_modules
**/target
dist
documentation
-17
View File
@@ -14,20 +14,3 @@ updates:
prefix: build prefix: build
prefix-development: chore prefix-development: chore
include: scope include: scope
# Update the root workspace (only). For now we don't include
# the contracts workspcae.
- package-ecosystem: cargo
directory: /
schedule:
interval: weekly
time: "09:00"
ignore:
- dependency-name: "cosmwasm-*"
- dependency-name: "cw*"
groups:
patch-updates:
patterns:
- "*"
update-types:
- "patch"
open-pull-requests-limit: 10
-39
View File
@@ -1,39 +0,0 @@
name: build-deb-meta
on:
workflow_dispatch:
jobs:
build:
runs-on: arc-ubuntu-22.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Build Environment
run: sudo apt-get update && sudo apt-get install -y make dpkg-dev
- name: Build Debian Packages
working-directory: ppa/packages
run: make
- name: Find .deb files
working-directory: ppa/packages
run: |
echo "file1=$(ls nym-repo-setup*.deb)" >> $GITHUB_ENV
echo "file2=$(ls nym-vpn*.deb)" >> $GITHUB_ENV
- name: Upload nym-repo-setup
uses: actions/upload-artifact@v4
with:
name: ${{ env.file1 }}
path: ppa/packages/nym-repo-setup*.deb
retention-days: 10
- name: Upload nym-vpn
uses: actions/upload-artifact@v4
with:
name: ${{ env.file2 }}
path: ppa/packages/nym-vpn*.deb
retention-days: 10
+2 -2
View File
@@ -7,7 +7,7 @@ jobs:
build: build:
runs-on: ubuntu-20.04-16-core runs-on: ubuntu-20.04-16-core
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Install Dependencies (Linux) - 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 git python3 && sudo apt-get update --fix-missing run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
- name: Install pip3 - name: Install pip3
@@ -17,7 +17,7 @@ jobs:
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- name: Install Rust stable - name: Install Rust stable
@@ -13,7 +13,6 @@ on:
- 'mixnode/**' - 'mixnode/**'
- 'sdk/rust/nym-sdk/**' - 'sdk/rust/nym-sdk/**'
- 'service-providers/**' - 'service-providers/**'
- '.github/workflows/ci-binary-config-checker.yml'
pull_request: pull_request:
paths: paths:
- 'clients/**' - 'clients/**'
@@ -23,7 +22,6 @@ on:
- 'mixnode/**' - 'mixnode/**'
- 'sdk/rust/nym-sdk/**' - 'sdk/rust/nym-sdk/**'
- 'service-providers/**' - 'service-providers/**'
- '.github/workflows/ci-binary-config-checker.yml'
env: env:
NETWORK: mainnet NETWORK: mainnet
@@ -33,11 +31,11 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [arc-ubuntu-20.04] platform: [custom-linux]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Install Dependencies (Linux) - name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install jq vim libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools run: sudo apt-get update && sudo apt-get -y install jq vim libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
+2 -3
View File
@@ -5,18 +5,17 @@ on:
paths: paths:
- "ts-packages/**" - "ts-packages/**"
- "sdk/typescript/**" - "sdk/typescript/**"
- ".github/workflows/ci-build-ts.yml"
jobs: jobs:
build: build:
runs-on: ubuntu-20.04-16-core runs-on: ubuntu-20.04-16-core
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
continue-on-error: true continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- name: Setup yarn - name: Setup yarn
+15 -4
View File
@@ -8,6 +8,11 @@ on:
required: true required: true
default: false default: false
type: boolean type: boolean
enable_wireguard:
description: "Add --features wireguard"
required: true
default: false
type: boolean
enable_deb: enable_deb:
description: "True to enable cargo-deb installation and .deb package building" description: "True to enable cargo-deb installation and .deb package building"
required: false required: false
@@ -31,20 +36,19 @@ on:
- "service-providers/**" - "service-providers/**"
- "tools/**" - "tools/**"
- "nymvisor/**" - "nymvisor/**"
- ".github/workflows/ci-build-upload-binaries.yml"
jobs: jobs:
publish-nym: publish-nym:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [ arc-ubuntu-20.04 ] platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Prepare build output directory - name: Prepare build output directory
shell: bash shell: bash
@@ -63,6 +67,13 @@ jobs:
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
- name: Set CARGO_FEATURES
run: |
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
if: >
github.event_name == 'schedule' ||
(github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true)
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
@@ -88,7 +99,7 @@ jobs:
- name: Upload Artifact - name: Upload Artifact
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: nym-binaries-artifacts name: nym-binaries-artifacts
path: | path: |
+36 -29
View File
@@ -1,6 +1,23 @@
name: ci-build name: ci-build
on: on:
push:
paths:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/lib/socks5-listener/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- 'nym-api/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'Cargo.toml'
pull_request: pull_request:
paths: paths:
- 'clients/**' - 'clients/**'
@@ -9,19 +26,15 @@ on:
- 'gateway/**' - 'gateway/**'
- 'integrations/**' - 'integrations/**'
- 'mixnode/**' - 'mixnode/**'
- 'sdk/rust/**' - 'sdk/lib/socks5-listener/**'
- 'sdk/lib/**' - 'sdk/rust/nym-sdk/**'
- 'service-providers/**' - 'service-providers/**'
- 'nym-network-monitor/**'
- 'nym-api/**' - 'nym-api/**'
- 'nym-node/**'
- 'nym-outfox/**' - 'nym-outfox/**'
- 'nym-validator-rewarder/**' - 'tools/nym-cli/**'
- 'tools/**' - 'tools/nym-nr-query/**'
- 'wasm/**' - 'tools/ts-rs-cli/**'
- 'Cargo.toml' - 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci-build.yml'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -29,7 +42,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [arc-ubuntu-20.04, custom-runner-mac-m1] os: [custom-linux, custom-runner-mac-m1]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
@@ -37,10 +50,10 @@ jobs:
- name: Install Dependencies (Linux) - 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 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 continue-on-error: true
if: contains(matrix.os, 'ubuntu') if: matrix.os == 'custom-linux'
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Install rust toolchain - name: Install rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -60,46 +73,40 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
# Enable wireguard by default on linux only
# while disabled by default, this build ensures nothing is broken within args: --workspace --features wireguard
# `axum` feature
- name: Build with `axum` feature
uses: actions-rs/cargo@v1
with:
command: build
args: --features axum
- name: Build all examples - name: Build all examples
if: contains(matrix.os, 'ubuntu') if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --workspace --examples args: --workspace --examples --features wireguard
- name: Run all tests - name: Run all tests
if: contains(matrix.os, 'ubuntu') if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --workspace args: --workspace --features wireguard
- name: Run expensive tests - name: Run expensive tests
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && contains(matrix.os, 'ubuntu') if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --workspace -- --ignored args: --workspace --features wireguard -- --ignored
- name: Annotate with clippy checks - name: Annotate with clippy checks
if: contains(matrix.os, 'ubuntu') if: matrix.os == 'custom-linux'
uses: actions-rs/clippy-check@v1 uses: actions-rs/clippy-check@v1
continue-on-error: true continue-on-error: true
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace args: --workspace --features wireguard
- name: Clippy - name: Clippy
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: clippy command: clippy
args: --workspace --all-targets --features axum -- -D warnings args: --workspace --all-targets --features wireguard -- -D warnings
+2 -6
View File
@@ -2,14 +2,10 @@ name: ci-cargo-deny
on: on:
workflow_dispatch: workflow_dispatch:
pull_request: pull_request:
paths:
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci-cargo-deny.yml'
jobs: jobs:
cargo-deny: cargo-deny:
runs-on: arc-ubuntu-22.04-dind runs-on: ubuntu-22.04
strategy: strategy:
matrix: matrix:
checks: checks:
@@ -17,7 +13,7 @@ jobs:
- licenses bans sources - licenses bans sources
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: EmbarkStudios/cargo-deny-action@v1 - uses: EmbarkStudios/cargo-deny-action@v1
with: with:
log-level: warn log-level: warn
+2 -3
View File
@@ -6,17 +6,16 @@ on:
paths: paths:
- 'contracts/**' - 'contracts/**'
- 'common/**' - 'common/**'
- '.github/workflows/ci-contracts-schema.yml'
jobs: jobs:
check-schema: check-schema:
name: Generate and check schema name: Generate and check schema
runs-on: arc-ubuntu-20.04 runs-on: custom-linux
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -6,7 +6,6 @@ on:
paths: paths:
- 'common/**' - 'common/**'
- 'contracts/**' - 'contracts/**'
- '.github/workflows/ci-contracts-upload-binaries.yml'
env: env:
NETWORK: mainnet NETWORK: mainnet
@@ -16,13 +15,13 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: arc-ubuntu-20.04 platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Prepare build output directory - name: Prepare build output directory
shell: bash shell: bash
@@ -59,7 +58,6 @@ jobs:
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ecash.wasm $OUTPUT_DIR
- name: Deploy branch to CI www - name: Deploy branch to CI www
continue-on-error: true continue-on-error: true
+3 -4
View File
@@ -9,16 +9,15 @@ on:
paths: paths:
- 'contracts/**' - 'contracts/**'
- 'common/**' - 'common/**'
- '.github/workflows/ci-contracts.yml'
jobs: jobs:
matrix_prep: matrix_prep:
runs-on: arc-ubuntu-20.04 runs-on: ubuntu-20.04
outputs: outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }} matrix: ${{ steps.set-matrix.outputs.matrix }}
steps: steps:
# creates the matrix strategy from ci-contracts-matrix-includes.json # creates the matrix strategy from ci-contracts-matrix-includes.json
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- id: set-matrix - id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main uses: JoshuaTheMiller/conditional-build-matrix@main
with: with:
@@ -35,7 +34,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Setup rust - name: Setup rust
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
+2 -3
View File
@@ -6,13 +6,12 @@ on:
branches-ignore: master branches-ignore: master
paths: paths:
- 'documentation/docs/**' - 'documentation/docs/**'
- '.github/workflows/ci-docs.yml'
jobs: jobs:
build: build:
runs-on: ubuntu-20.04-16-core runs-on: ubuntu-20.04-16-core
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Install Dependencies (Linux) - 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 git python3 && sudo apt-get update --fix-missing run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
- name: Install pip3 - name: Install pip3
@@ -22,7 +21,7 @@ jobs:
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- name: Install Rust stable - name: Install Rust stable
+3 -4
View File
@@ -10,16 +10,15 @@ on:
- "nym-wallet/src/**" - "nym-wallet/src/**"
- "nym-wallet/package.json" - "nym-wallet/package.json"
- "explorer/**" - "explorer/**"
- ".github/workflows/ci-lint-typescript.yml"
jobs: jobs:
build: build:
runs-on: ubuntu-20.04-16-core runs-on: ubuntu-20.04-16-core
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- name: Setup yarn - name: Setup yarn
@@ -39,7 +38,7 @@ jobs:
version: '116' version: '116'
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v4
with: with:
go-version: '1.20' go-version: '1.20'
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
name: nym-api tests name: nym-api tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: install yarn in root - name: install yarn in root
run: cd ../.. && yarn install run: cd ../.. && yarn install
@@ -24,7 +24,7 @@ jobs:
run: npm install run: npm install
- name: Node v18 - name: Node v18
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 18.1.0 node-version: 18.1.0
@@ -5,7 +5,6 @@ on:
push: push:
paths: paths:
- 'explorer/**' - 'explorer/**'
- '.github/workflows/ci-nym-network-explorer.yml'
defaults: defaults:
run: run:
@@ -15,12 +14,12 @@ jobs:
build: build:
runs-on: custom-linux runs-on: custom-linux
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
continue-on-error: true continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- name: Setup yarn - name: Setup yarn
+8 -3
View File
@@ -1,17 +1,22 @@
name: ci-nym-wallet-rust name: ci-nym-wallet-rust
on: on:
push:
paths:
- 'nym-wallet/**'
- 'common/**'
- 'contracts/vesting/**'
- 'nym-api/nym-api-requests/**'
pull_request: pull_request:
paths: paths:
- 'nym-wallet/**' - 'nym-wallet/**'
- 'common/**' - 'common/**'
- 'contracts/vesting/**' - 'contracts/vesting/**'
- 'nym-api/nym-api-requests/**' - 'nym-api/nym-api-requests/**'
- '.github/workflows/ci-nym-wallet-rust.yml'
jobs: jobs:
build: build:
runs-on: arc-ubuntu-20.04 runs-on: [ self-hosted, custom-linux ]
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
steps: steps:
@@ -20,7 +25,7 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Install rust toolchain - name: Install rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -4,13 +4,12 @@ on:
pull_request: pull_request:
paths: paths:
- 'nym-wallet/**' - 'nym-wallet/**'
- '.github/workflows/ci-nym-wallet-storybook.yml'
jobs: jobs:
build: build:
runs-on: custom-linux runs-on: custom-linux
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
@@ -18,7 +17,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
+3 -4
View File
@@ -5,18 +5,17 @@ on:
paths: paths:
- "sdk/typescript/**" - "sdk/typescript/**"
- "wasm/**" - "wasm/**"
- '.github/workflows/ci-sdk-docs-typescript.yml'
jobs: jobs:
build: build:
runs-on: custom-linux runs-on: custom-linux
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
continue-on-error: true continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18.17 node-version: 18.17
- name: Install Rust stable - name: Install Rust stable
@@ -27,7 +26,7 @@ jobs:
run: npm install -g yarn run: npm install -g yarn
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v4
with: with:
go-version: '1.20' go-version: '1.20'
+4 -5
View File
@@ -6,17 +6,16 @@ on:
- 'wasm/**' - 'wasm/**'
- 'clients/client-core/**' - 'clients/client-core/**'
- 'common/**' - 'common/**'
- '.github/workflows/ci-sdk-wasm.yml'
jobs: jobs:
wasm: wasm:
runs-on: arc-ubuntu-20.04 runs-on: [custom-linux]
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
@@ -29,7 +28,7 @@ jobs:
components: rustfmt, clippy components: rustfmt, clippy
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v4
with: with:
go-version: '1.20' go-version: '1.20'
+4 -4
View File
@@ -30,14 +30,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v3
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v2
with: with:
# Upload entire repository # Upload entire repository
path: './ppa' path: './ppa'
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v2
+5 -5
View File
@@ -18,7 +18,7 @@ jobs:
continue-on-error: true continue-on-error: true
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Install Dependencies (Linux) - 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 run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
@@ -33,7 +33,7 @@ jobs:
components: rustfmt, clippy components: rustfmt, clippy
- name: Install Protoc - name: Install Protoc
uses: arduino/setup-protoc@v3 uses: arduino/setup-protoc@v2
if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest' if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest'
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
@@ -98,11 +98,11 @@ jobs:
runs-on: custom-linux runs-on: custom-linux
steps: steps:
- name: Collect jobs status - name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v3 uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: install npm - name: install npm
uses: actions/setup-node@v4 uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
with: with:
node-version: 18 node-version: 18
@@ -15,7 +15,7 @@ jobs:
output1: ${{ steps.step2.outputs.latest_release }} output1: ${{ steps.step2.outputs.latest_release }}
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set output variable to latest release branch - name: Set output variable to latest release branch
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup git user - name: Setup git user
@@ -47,7 +47,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup git user - name: Setup git user
@@ -18,7 +18,7 @@ jobs:
continue-on-error: true continue-on-error: true
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Install Dependencies (Linux) - 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 squashfs-tools run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
@@ -61,11 +61,11 @@ jobs:
runs-on: custom-linux runs-on: custom-linux
steps: steps:
- name: Collect jobs status - name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v3 uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: install npm - name: install npm
uses: actions/setup-node@v4 uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure' if: env.WORKFLOW_CONCLUSION == 'failure'
with: with:
node-version: 18 node-version: 18
+5 -5
View File
@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout repository code - name: Checkout repository code
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Install rust toolchain - name: Install rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
@@ -20,7 +20,7 @@ jobs:
find . -name Cargo.toml -exec cargo deny --manifest-path {} check \ find . -name Cargo.toml -exec cargo deny --manifest-path {} check \
advisories -A advisory-not-detected --hide-inclusion-graph \; &> \ advisories -A advisory-not-detected --hide-inclusion-graph \; &> \
>(uniq &> .github/workflows/support-files/notifications/deny.message ) >(uniq &> .github/workflows/support-files/notifications/deny.message )
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
with: with:
name: report name: report
path: .github/workflows/support-files/notifications/deny.message path: .github/workflows/support-files/notifications/deny.message
@@ -29,14 +29,14 @@ jobs:
runs-on: custom-linux runs-on: custom-linux
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Download report from previous job - name: Download report from previous job
uses: actions/download-artifact@v4 uses: actions/download-artifact@v3
with: with:
name: report name: report
path: .github/workflows/support-files/notifications path: .github/workflows/support-files/notifications
- name: install npm - name: install npm
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- name: Matrix - Node Install - name: Matrix - Node Install
+5 -5
View File
@@ -40,7 +40,7 @@ jobs:
cli_version: ${{ steps.binary-versions.outputs.cli_version }} cli_version: ${{ steps.binary-versions.outputs.cli_version }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Install Dependencies (Linux) - name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install ripgrep libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools run: sudo apt-get update && sudo apt-get -y install ripgrep libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
@@ -60,10 +60,10 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --workspace --release ${{ env.CARGO_FEATURES }} args: --workspace --release
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: my-artifact name: my-artifact
path: | path: |
@@ -79,7 +79,7 @@ jobs:
- id: create-release - id: create-release
name: Upload to release based on tag name name: Upload to release based on tag name
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: github.event_name == 'release' if: github.event_name == 'release'
with: with:
files: | files: |
+4 -4
View File
@@ -9,7 +9,7 @@ jobs:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} 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-ubuntu-20.04]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -26,21 +26,21 @@ jobs:
run: make contracts run: make contracts
- name: Upload Mixnet Contract Artifact - name: Upload Mixnet Contract Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: mixnet_contract.wasm name: mixnet_contract.wasm
path: contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm path: contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
retention-days: 5 retention-days: 5
- name: Upload Vesting Contract Artifact - name: Upload Vesting Contract Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: vesting_contract.wasm name: vesting_contract.wasm
path: contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm path: contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
retention-days: 5 retention-days: 5
- name: Upload to release based on tag name - name: Upload to release based on tag name
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: github.event_name == 'release' if: github.event_name == 'release'
with: with:
files: | files: |
@@ -25,10 +25,10 @@ jobs:
file_hash: ${{ steps.release-info.outputs.file_hash }} file_hash: ${{ steps.release-info.outputs.file_hash }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Node - name: Node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- name: Install Rust stable - name: Install Rust stable
@@ -83,7 +83,7 @@ jobs:
run: yarn && yarn build run: yarn && yarn build
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: nym-wallet.app.tar.gz name: nym-wallet.app.tar.gz
path: nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz path: nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz
@@ -95,7 +95,7 @@ jobs:
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
- id: create-release - id: create-release
name: Upload to release based on tag name name: Upload to release based on tag name
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: github.event_name == 'release' if: github.event_name == 'release'
with: with:
files: | files: |
@@ -25,7 +25,7 @@ jobs:
file_hash: ${{ steps.release-info.outputs.file_hash }} file_hash: ${{ steps.release-info.outputs.file_hash }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Tauri dependencies - name: Tauri dependencies
run: > run: >
@@ -34,7 +34,7 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Node - name: Node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
@@ -62,7 +62,7 @@ jobs:
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: nym-wallet_1.0.0_amd64.AppImage.tar.gz name: nym-wallet_1.0.0_amd64.AppImage.tar.gz
path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz
@@ -70,7 +70,7 @@ jobs:
- id: create-release - id: create-release
name: Upload to release based on tag name name: Upload to release based on tag name
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: github.event_name == 'release' if: github.event_name == 'release'
with: with:
files: | files: |
@@ -1,4 +1,4 @@
name: publish-nym-wallet-win11 name: publish-nym-wallet-win10
on: on:
workflow_dispatch: workflow_dispatch:
release: release:
@@ -14,7 +14,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [custom-windows-11] platform: [windows10]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
outputs: outputs:
@@ -33,7 +33,7 @@ jobs:
del /s /q /A:H nym del /s /q /A:H nym
rmdir /s /q nym rmdir /s /q nym
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Import signing certificate - name: Import signing certificate
env: env:
@@ -47,7 +47,7 @@ jobs:
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText) Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
- name: Node - name: Node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
@@ -62,9 +62,6 @@ jobs:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install Yarn
run: npm install -g yarn
- name: Install project dependencies - name: Install project dependencies
shell: bash shell: bash
run: cd .. && yarn --network-timeout 100000 run: cd .. && yarn --network-timeout 100000
@@ -85,7 +82,7 @@ jobs:
run: yarn build run: yarn build
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: nym-wallet_1.0.0_x64_en-US.msi name: nym-wallet_1.0.0_x64_en-US.msi
path: nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi path: nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi
@@ -93,7 +90,7 @@ jobs:
- id: create-release - id: create-release
name: Upload to release based on tag name name: Upload to release based on tag name
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: github.event_name == 'release' if: github.event_name == 'release'
with: with:
files: | files: |
@@ -22,10 +22,10 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Install Java - name: Install Java
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
distribution: "temurin" distribution: "temurin"
java-version: "17" java-version: "17"
@@ -49,7 +49,7 @@ jobs:
"build-tools;$SDK_BUILDTOOLS_VERSION" "build-tools;$SDK_BUILDTOOLS_VERSION"
- name: Install Rust toolchain - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@1.90.0 uses: dtolnay/rust-toolchain@1.70.0
- name: Install rust android targets - name: Install rust android targets
run: | run: |
@@ -84,7 +84,7 @@ jobs:
apk/nyms5-arch64-release.apk apk/nyms5-arch64-release.apk
- name: Upload APKs - name: Upload APKs
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: nyms5-apk-arch64 name: nyms5-apk-arch64
path: | path: |
@@ -97,14 +97,14 @@ jobs:
runs-on: custom-linux runs-on: custom-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Download binary artifact - name: Download binary artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v3
with: with:
name: nyms5-apk-arch64 name: nyms5-apk-arch64
path: apk path: apk
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
with: with:
files: | files: |
apk/nyms5-arch64-debug.apk apk/nyms5-arch64-debug.apk
+4 -4
View File
@@ -6,10 +6,10 @@ jobs:
publish: publish:
runs-on: ubuntu-20.04-16-core runs-on: ubuntu-20.04-16-core
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Install Node - name: Install Node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
registry-url: "https://registry.npmjs.org" registry-url: "https://registry.npmjs.org"
@@ -29,12 +29,12 @@ jobs:
run: cargo install wasm-opt run: cargo install wasm-opt
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v4
with: with:
go-version: "1.20" go-version: "1.20"
- name: Install TinyGo - name: Install TinyGo
uses: acifani/setup-tinygo@v2 uses: acifani/setup-tinygo@v1
with: with:
tinygo-version: "0.27.0" tinygo-version: "0.27.0"
+3 -3
View File
@@ -20,8 +20,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v3
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- uses: nymtech/nym/.github/actions/nym-hash-releases@develop - uses: nymtech/nym/.github/actions/nym-hash-releases@develop
@@ -30,7 +30,7 @@ jobs:
with: with:
release-tag-or-name-or-id: ${{ inputs.release_tag }} release-tag-or-name-or-id: ${{ inputs.release_tag }}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v2
with: with:
name: Asset Hashes name: Asset Hashes
path: hashes.json path: hashes.json
+1 -4
View File
@@ -48,7 +48,4 @@ foxyfox.env
.next .next
ppa-private-key.b64 ppa-private-key.b64
ppa-private-key.asc ppa-private-key.asc
nym-network-monitor/topology.json
nym-network-monitor/__pycache__
nym-network-monitor/*.key
+1 -92
View File
@@ -4,98 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [2024.10-caramello] (2024-09-10)
- Backport 4844 and 4845 ([#4857])
- Bugfix/client registration vol2 ([#4856])
- Remove wireguard feature flag and pass runtime enabled flag ([#4839])
- Eliminate cancel unsafe sig awaiting ([#4834])
- added explicit updateable admin to the mixnet contract ([#4822])
- using legacy signing payload in CLI and verifying both variants in contract ([#4821])
- adding ecash contract address ([#4819])
- Check profit margin of node before defaulting to hardcoded value ([#4802])
- Sync last_seen_bandwidth immediately ([#4774])
- Feature/additional ecash nym cli utils ([#4773])
- Better storage error logging ([#4772])
- bugfix: make sure DKG parses data out of events if logs are empty ([#4764])
- Fix clippy on rustc beta toolchain ([#4746])
- Fix clippy for beta toolchain ([#4742])
- Disable testnet-manager on non-unix ([#4741])
- Don't set NYM_VPN_API to default ([#4740])
- Update publish-nym-binaries.yml ([#4739])
- Update ci-build-upload-binaries.yml ([#4738])
- Add NYM_VPN_API to network config ([#4736])
- Re-export RecipientFormattingError in nym sdk ([#4735])
- Persist wireguard peers ([#4732])
- Fix tokio error in 1.39 ([#4730])
- Feature/vesting purge plus ranged cost params ([#4716])
- Fix (some) feature unification build failures ([#4681])
- Feature Compact Ecash : The One PR ([#4623])
[#4857]: https://github.com/nymtech/nym/pull/4857
[#4856]: https://github.com/nymtech/nym/pull/4856
[#4839]: https://github.com/nymtech/nym/pull/4839
[#4834]: https://github.com/nymtech/nym/pull/4834
[#4822]: https://github.com/nymtech/nym/pull/4822
[#4821]: https://github.com/nymtech/nym/pull/4821
[#4819]: https://github.com/nymtech/nym/pull/4819
[#4802]: https://github.com/nymtech/nym/pull/4802
[#4774]: https://github.com/nymtech/nym/pull/4774
[#4773]: https://github.com/nymtech/nym/pull/4773
[#4772]: https://github.com/nymtech/nym/pull/4772
[#4764]: https://github.com/nymtech/nym/pull/4764
[#4746]: https://github.com/nymtech/nym/pull/4746
[#4742]: https://github.com/nymtech/nym/pull/4742
[#4741]: https://github.com/nymtech/nym/pull/4741
[#4740]: https://github.com/nymtech/nym/pull/4740
[#4739]: https://github.com/nymtech/nym/pull/4739
[#4738]: https://github.com/nymtech/nym/pull/4738
[#4736]: https://github.com/nymtech/nym/pull/4736
[#4735]: https://github.com/nymtech/nym/pull/4735
[#4732]: https://github.com/nymtech/nym/pull/4732
[#4730]: https://github.com/nymtech/nym/pull/4730
[#4716]: https://github.com/nymtech/nym/pull/4716
[#4681]: https://github.com/nymtech/nym/pull/4681
[#4623]: https://github.com/nymtech/nym/pull/4623
## [2024.9-topdeck] (2024-07-26)
- chore: fix 1.80 lint issues ([#4731])
- Handle clients with different versions in IPR ([#4723])
- Add 1GB/day/user bandwidth cap ([#4717])
- Feature/merge back ([#4710])
- removed mixnode/gateway config migration code and disabled cli without explicit flag ([#4706])
[#4731]: https://github.com/nymtech/nym/pull/4731
[#4723]: https://github.com/nymtech/nym/pull/4723
[#4717]: https://github.com/nymtech/nym/pull/4717
[#4710]: https://github.com/nymtech/nym/pull/4710
[#4706]: https://github.com/nymtech/nym/pull/4706
## [2024.8-wispa] (2024-07-10)
- add event parsing to support cosmos_sdk > 0.50 ([#4697])
- Fix NR config compatibility ([#4690])
- Remove UserAgent constructor since it's weakly typed ([#4689])
- [bugfix]: Node_api_check CLI looked over roles on blacklisted nodes ([#4687])
- Add mixnodes to self describing api cache ([#4684])
- Move and whole bump of crates to workspace and upgrade some ([#4680])
- Remove code that refers to removed nym-network-statistics ([#4679])
- Remove nym-network-statistics ([#4678])
- Create UserAgent that can be passed from the binary to the nym api client ([#4677])
- Add authenticator ([#4667])
[#4697]: https://github.com/nymtech/nym/pull/4697
[#4690]: https://github.com/nymtech/nym/pull/4690
[#4689]: https://github.com/nymtech/nym/pull/4689
[#4687]: https://github.com/nymtech/nym/pull/4687
[#4684]: https://github.com/nymtech/nym/pull/4684
[#4680]: https://github.com/nymtech/nym/pull/4680
[#4679]: https://github.com/nymtech/nym/pull/4679
[#4678]: https://github.com/nymtech/nym/pull/4678
[#4677]: https://github.com/nymtech/nym/pull/4677
[#4667]: https://github.com/nymtech/nym/pull/4667
## [2024.7-doubledecker] (2024-07-04) ## [2024.7-doubledecker] (2024-07-04)
- Add an early return in `parse_raw_str_logs` for empty raw log strings. ([#4686]) - Add an early return in `parse_raw_str_logs` for empty raw log strings. ([#4686])
@@ -605,6 +513,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#3187]: https://github.com/nymtech/nym/issues/3187 [#3187]: https://github.com/nymtech/nym/issues/3187
[#3203]: https://github.com/nymtech/nym/pull/3203 [#3203]: https://github.com/nymtech/nym/pull/3203
[#3199]: https://github.com/nymtech/nym/pull/3199 [#3199]: https://github.com/nymtech/nym/pull/3199
>>>>>>> master
## [v1.1.13] (2023-03-15) ## [v1.1.13] (2023-03-15)
Generated
+811 -1582
View File
File diff suppressed because it is too large Load Diff
+54 -83
View File
@@ -14,6 +14,7 @@ panic = "abort"
opt-level = 3 opt-level = 3
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ members = [
"clients/native", "clients/native",
@@ -33,7 +34,6 @@ members = [
"common/commands", "common/commands",
"common/config", "common/config",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/ecash-contract",
"common/cosmwasm-smart-contracts/coconut-dkg", "common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/group-contract",
@@ -45,15 +45,10 @@ members = [
"common/credentials", "common/credentials",
"common/credential-utils", "common/credential-utils",
"common/credentials-interface", "common/credentials-interface",
"common/credential-verification",
"common/crypto", "common/crypto",
"common/dkg", "common/dkg",
"common/ecash-double-spending",
"common/ecash-time",
"common/execute", "common/execute",
"common/exit-policy", "common/exit-policy",
"common/gateway-requests",
"common/gateway-storage",
"common/http-api-client", "common/http-api-client",
"common/http-api-common", "common/http-api-common",
"common/inclusion-probability", "common/inclusion-probability",
@@ -64,7 +59,6 @@ members = [
"common/node-tester-utils", "common/node-tester-utils",
"common/nonexhaustive-delayqueue", "common/nonexhaustive-delayqueue",
"common/nymcoconut", "common/nymcoconut",
"common/nym_offline_compact_ecash",
"common/nym-id", "common/nym-id",
"common/nym-metrics", "common/nym-metrics",
"common/nymsphinx", "common/nymsphinx",
@@ -80,7 +74,6 @@ members = [
"common/nymsphinx/types", "common/nymsphinx/types",
"common/nyxd-scraper", "common/nyxd-scraper",
"common/pemstore", "common/pemstore",
"common/serde-helpers",
"common/socks5-client-core", "common/socks5-client-core",
"common/socks5/proxy-helpers", "common/socks5/proxy-helpers",
"common/socks5/requests", "common/socks5/requests",
@@ -98,6 +91,7 @@ members = [
"explorer-api/explorer-api-requests", "explorer-api/explorer-api-requests",
"explorer-api/explorer-client", "explorer-api/explorer-client",
"gateway", "gateway",
"gateway/gateway-requests",
"integrations/bity", "integrations/bity",
"mixnode", "mixnode",
"sdk/lib/socks5-listener", "sdk/lib/socks5-listener",
@@ -106,7 +100,6 @@ members = [
"service-providers/common", "service-providers/common",
"service-providers/ip-packet-router", "service-providers/ip-packet-router",
"service-providers/network-requester", "service-providers/network-requester",
"nym-network-monitor",
"nym-api", "nym-api",
"nym-browser-extension/storage", "nym-browser-extension/storage",
"nym-api/nym-api-requests", "nym-api/nym-api-requests",
@@ -117,8 +110,6 @@ members = [
"nym-validator-rewarder", "nym-validator-rewarder",
"tools/internal/ssl-inject", "tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump", # "tools/internal/sdk-version-bump",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/nym-cli", "tools/nym-cli",
"tools/nym-id-cli", "tools/nym-id-cli",
"tools/nym-nr-query", "tools/nym-nr-query",
@@ -134,23 +125,20 @@ members = [
default-members = [ default-members = [
"clients/native", "clients/native",
"clients/socks5", "clients/socks5",
"explorer-api",
"gateway", "gateway",
"service-providers/network-requester",
"mixnode", "mixnode",
"nym-api", "nym-api",
"nym-node",
"nym-validator-rewarder",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"tools/nymvisor", "tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
"nym-node"
] ]
exclude = [ exclude = [
"explorer", "explorer",
"contracts", "contracts",
"nym-wallet", "nym-wallet",
"nym-vpn/ui/src-tauri",
"cpu-cycles", "cpu-cycles",
"sdk/ffi/cpp", "sdk/ffi/cpp",
] ]
@@ -162,31 +150,24 @@ homepage = "https://nymtech.net"
documentation = "https://nymtech.net" documentation = "https://nymtech.net"
edition = "2021" edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
rust-version = "1.80"
readme = "README.md"
[workspace.dependencies] [workspace.dependencies]
addr = "0.15.6" addr = "0.15.6"
aes = "0.8.1" aes = "0.8.1"
aes-gcm = "0.10.1" aes-gcm = "0.10.1"
anyhow = "1.0.87" anyhow = "1.0.71"
argon2 = "0.5.0" argon2 = "0.5.0"
async-trait = "0.1.82" async-trait = "0.1.68"
axum = "0.7.5" axum = "0.7.5"
axum-extra = "0.9.3" axum-extra = "0.9.3"
base64 = "0.22.1" base64 = "0.21.4"
bincode = "1.3.3" bincode = "1.3.3"
bip39 = { version = "2.0.0", features = ["zeroize"] } bip39 = { version = "2.0.0", features = ["zeroize"] }
# can we unify those?
bit-vec = "0.7.0"
bitvec = "1.0.0" bitvec = "1.0.0"
blake3 = "1.3.1"
blake3 = "1.5.4"
bloomfilter = "1.0.14"
bs58 = "0.5.1" bs58 = "0.5.1"
bytecodec = "0.4.15" bytecodec = "0.4.15"
bytes = "1.7.1" bytes = "1.5.0"
cargo_metadata = "0.18.1" cargo_metadata = "0.18.1"
celes = "2.4.0" celes = "2.4.0"
cfg-if = "1.0.0" cfg-if = "1.0.0"
@@ -194,16 +175,15 @@ chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1" chacha20poly1305 = "0.10.1"
chrono = "0.4.31" chrono = "0.4.31"
cipher = "0.4.3" cipher = "0.4.3"
clap = "4.5.17" clap = "4.4.7"
clap_complete = "4.5" clap_complete = "4.0"
clap_complete_fig = "4.5" clap_complete_fig = "4.0"
colored = "2.0" colored = "2.0"
comfy-table = "6.0.0" comfy-table = "6.0.0"
console = "0.15.8"
console-subscriber = "0.1.1" console-subscriber = "0.1.1"
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
const-str = "0.5.6" const-str = "0.5.6"
const_format = "0.2.33" const_format = "0.2.32"
criterion = "0.4" criterion = "0.4"
csv = "1.3.0" csv = "1.3.0"
ctr = "0.9.1" ctr = "0.9.1"
@@ -212,15 +192,15 @@ curve25519-dalek = "4.1"
dashmap = "5.5.3" dashmap = "5.5.3"
defguard_wireguard_rs = "0.4.2" defguard_wireguard_rs = "0.4.2"
digest = "0.10.7" digest = "0.10.7"
dirs = "5.0" dirs = "4.0"
doc-comment = "0.3" doc-comment = "0.3"
dotenvy = "0.15.6" dotenvy = "0.15.6"
ecdsa = "0.16" ecdsa = "0.16"
ed25519-dalek = "2.1" ed25519-dalek = "2.1"
etherparse = "0.13.0" etherparse = "0.13.0"
eyre = "0.6.9" eyre = "0.6.9"
fastrand = "2.1.1" fastrand = "2.1.0"
flate2 = "1.0.33" flate2 = "1.0.28"
futures = "0.3.28" futures = "0.3.28"
generic-array = "0.14.7" generic-array = "0.14.7"
getrandom = "0.2.10" getrandom = "0.2.10"
@@ -236,12 +216,11 @@ httpcodec = "0.2.3"
humantime = "2.1.0" humantime = "2.1.0"
humantime-serde = "1.1.1" humantime-serde = "1.1.1"
hyper = "1.3.1" hyper = "1.3.1"
indicatif = "0.17.8" indexed_db_futures = "0.3.0"
inquire = "0.6.2" inquire = "0.6.2"
ip_network = "0.4.1" ip_network = "0.4.1"
ipnetwork = "0.20" ipnetwork = "0.16"
isocountry = "0.3.2" isocountry = "0.3.2"
itertools = "0.13.0"
k256 = "0.13" k256 = "0.13"
lazy_static = "1.4.0" lazy_static = "1.4.0"
ledger-transport = "0.10.0" ledger-transport = "0.10.0"
@@ -255,53 +234,54 @@ okapi = "0.7.0"
once_cell = "1.7.2" once_cell = "1.7.2"
opentelemetry = "0.19.0" opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0" opentelemetry-jaeger = "0.18.0"
parking_lot = "0.12.3" parking_lot = "0.12.1"
pem = "0.8" pem = "0.8"
petgraph = "0.6.5"
pin-project = "1.0" pin-project = "1.0"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
publicsuffix = "2.2.3" publicsuffix = "2.2.3"
quote = "1" quote = "1"
rand = "0.8.5" rand = "0.8.5"
rand-07 = "0.7.3"
rand_chacha = "0.3" rand_chacha = "0.3"
rand_chacha_02 = "0.2"
rand_core = "0.6.3" rand_core = "0.6.3"
rand_distr = "0.4" rand_distr = "0.4"
rand_pcg = "0.3.1" rand_pcg = "0.3.1"
rand_seeder = "0.2.3" rand_seeder = "0.2.3"
rayon = "1.5.1" rayon = "1.5.1"
regex = "1.10.6" regex = "1.8.4"
reqwest = { version = "0.12.4", default-features = false } reqwest = { version = "0.12.4", default-features = false }
rocket = "0.5.0" rocket = "0.5.0"
rocket_cors = "0.6.0" rocket_cors = "0.6.0"
rocket_okapi = "0.8.0" rocket_okapi = "0.8.0"
safer-ffi = "0.1.12" safer-ffi = "0.1.4"
schemars = "0.8.21" schemars = "0.8.1"
semver = "1.0.23" semver = "1.0.23"
serde = "1.0.210" serde = "1.0.152"
serde_bytes = "0.11.15" serde_bytes = "0.11.6"
serde_derive = "1.0" serde_derive = "1.0"
serde_json = "1.0.128" serde_json = "1.0.91"
serde_repr = "0.1" serde_repr = "0.1"
serde_with = "3.9.0" serde_with = "3.4.0"
serde_yaml = "0.9.25" serde_yaml = "0.9.25"
sha2 = "0.10.8" sha2 = "0.10.8"
si-scale = "0.2.3" si-scale = "0.2.2"
sphinx-packet = "0.1.1" sphinx-packet = "0.1.1"
sqlx = "0.6.3" sqlx = "0.6.3"
strum = "0.26" strum = "0.25"
subtle-encoding = "0.5" subtle-encoding = "0.5"
syn = "1" syn = "1"
sysinfo = "0.30.12" sysinfo = "0.30.12"
tap = "1.0.1" tap = "1.0.1"
tar = "0.4.41" tar = "0.4.40"
tempfile = "3.5.0" tempfile = "3.5.0"
thiserror = "1.0.63" thiserror = "1.0.48"
time = "0.3.30" time = "0.3.30"
tokio = "1.39" tokio = "1.33.0"
tokio-stream = "0.1.16" tokio-stream = "0.1.14"
tokio-test = "0.4.4" tokio-test = "0.4.2"
tokio-tungstenite = { version = "0.20.1" } tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.12" tokio-util = "0.7.10"
toml = "0.8.14" toml = "0.8.14"
tower = "0.4.13" tower = "0.4.13"
tower-http = "0.5.2" tower-http = "0.5.2"
@@ -311,15 +291,12 @@ tracing-subscriber = "0.3.16"
tracing-tree = "0.2.2" tracing-tree = "0.2.2"
ts-rs = "7.0.0" ts-rs = "7.0.0"
tungstenite = { version = "0.20.1", default-features = false } tungstenite = { version = "0.20.1", default-features = false }
url = "2.5" url = "2.4"
utoipa = "4.2" utoipa = "4.2.0"
utoipa-rapidoc = "4.0" utoipa-swagger-ui = "6.0.0"
utoipa-swagger-ui = "7.1"
utoipauto = "0.1"
uuid = "*"
vergen = { version = "=8.3.1", default-features = false } vergen = { version = "=8.3.1", default-features = false }
walkdir = "2" walkdir = "2"
wasm-bindgen-test = "0.3.43" wasm-bindgen-test = "0.3.36"
x25519-dalek = "2.0.0" x25519-dalek = "2.0.0"
zeroize = "1.6.0" zeroize = "1.6.0"
@@ -328,8 +305,7 @@ prometheus = { version = "0.13.0" }
# coconut/DKG related # coconut/DKG related
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork # unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm # as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
# plus to make our live easier we need serde support from https://github.com/zkcrypto/bls12_381/pull/125 bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "feature/gt-serialization-0.8.0" }
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "temp/experimental-serdect" }
group = { version = "0.13.0", default-features = false } group = { version = "0.13.0", default-features = false }
ff = { version = "0.13.0", default-features = false } ff = { version = "0.13.0", default-features = false }
@@ -350,31 +326,26 @@ cw4 = { version = "=1.1.2" }
cw-controllers = { version = "=1.1.0" } cw-controllers = { version = "=1.1.0" }
# cosmrs-related # cosmrs-related
bip32 = { version = "0.5.2", default-features = false } bip32 = { version = "0.5.1", default-features = false }
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released) # temporarily using a fork again (yay.) because we need staking and slashing support
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged) cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" }
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" } #cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" } # unfortuntely we need a fork by yours truly to get the staking support
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support tendermint = "0.34" # same version as used by cosmrs
tendermint = "0.37.0" # same version as used by cosmrs tendermint-rpc = "0.34" # same version as used by cosmrs
tendermint-rpc = "0.37.0" # same version as used by cosmrs
prost = { version = "0.12", default-features = false } prost = { version = "0.12", default-features = false }
# wasm-related dependencies # wasm-related dependencies
gloo-utils = "0.2.0" gloo-utils = "0.2.0"
gloo-net = "0.5.0" gloo-net = "0.5.0"
js-sys = "0.3.69"
# use a separate branch due to feature unification failures
# this is blocked until the upstream removes outdates `wasm_bindgen` feature usage
# indexed_db_futures = "0.4.1"
indexed_db_futures = { git = "https://github.com/TiemenSch/rust-indexed-db", branch = "update-uuid" }
js-sys = "0.3.70"
serde-wasm-bindgen = "0.6.5" serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5" tsify = "0.4.5"
wasm-bindgen = "0.2.93" wasm-bindgen = "0.2.92"
wasm-bindgen-futures = "0.4.43" wasm-bindgen-futures = "0.4.39"
wasmtimer = "0.2.0" wasmtimer = "0.2.0"
web-sys = "0.3.70" web-sys = "0.3.69"
itertools = "0.12.0"
# Profile settings for individual crates # Profile settings for individual crates
+1 -1
View File
@@ -133,7 +133,7 @@ clippy: sdk-wasm-lint
# Build contracts ready for deploy # Build contracts ready for deploy
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
CONTRACTS=vesting_contract mixnet_contract nym_ecash CONTRACTS=vesting_contract mixnet_contract
CONTRACTS_WASM=$(addsuffix .wasm, $(CONTRACTS)) CONTRACTS_WASM=$(addsuffix .wasm, $(CONTRACTS))
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
+1 -1
View File
@@ -52,7 +52,7 @@ References for developers:
You can chat to us in two places: You can chat to us in two places:
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) * The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
* The various developer channels on [Discord](https://nymtech.net/go/discord) * The various developer channels on [Discord](https://discord.gg/FaTJb8q8)
### Tokenomics & Rewards ### Tokenomics & Rewards
+7 -23
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.40" version = "1.1.37"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
@@ -26,46 +26,30 @@ clap = { workspace = true, features = ["cargo", "derive"] }
dirs = { workspace = true } dirs = { workspace = true }
log = { workspace = true } # self explanatory log = { workspace = true } # self explanatory
rand = { workspace = true } rand = { workspace = true }
serde = { workspace = true, features = [ serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
"derive",
] } # for config serialization/deserialization
serde_json = { workspace = true } serde_json = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tap = { workspace = true } tap = { workspace = true }
time = { workspace = true } time = { workspace = true }
tokio = { workspace = true, features = [ tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
"rt-multi-thread",
"net",
"signal",
] } # async runtime
tokio-tungstenite = { workspace = true } tokio-tungstenite = { workspace = true }
zeroize = { workspace = true } zeroize = { workspace = true }
## internal ## internal
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-bin-common = { path = "../../common/bin-common", features = [ nym-bin-common = { path = "../../common/bin-common", features = ["output_format", "clap"] }
"output_format", nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
"clap",
] }
nym-client-core = { path = "../../common/client-core", features = [
"fs-credentials-storage",
"fs-surb-storage",
"fs-gateways-storage",
"cli",
] }
nym-config = { path = "../../common/config" } nym-config = { path = "../../common/config" }
nym-credential-storage = { path = "../../common/credential-storage" } nym-credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" } nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" } nym-crypto = { path = "../../common/crypto" }
nym-gateway-requests = { path = "../../common/gateway-requests" } nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../common/network-defaults" } nym-network-defaults = { path = "../../common/network-defaults" }
nym-sphinx = { path = "../../common/nymsphinx" } nym-sphinx = { path = "../../common/nymsphinx" }
nym-pemstore = { path = "../../common/pemstore" } nym-pemstore = { path = "../../common/pemstore" }
nym-task = { path = "../../common/task" } nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" } nym-topology = { path = "../../common/topology" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
"http-client",
] }
nym-client-websocket-requests = { path = "websocket-requests" } nym-client-websocket-requests = { path = "websocket-requests" }
nym-id = { path = "../../common/nym-id" } nym-id = { path = "../../common/nym-id" }
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_coin_index_signatures::{
import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportCoinIndexSignaturesArgs,
) -> Result<(), ClientError> {
import_coin_index_signatures::<CliNativeClient, _>(args).await?;
println!("successfully imported coin index signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{
import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportExpirationDateSignaturesArgs,
) -> Result<(), ClientError> {
import_expiration_date_signatures::<CliNativeClient, _>(args).await?;
println!("successfully imported expiration date signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_master_verification_key::{
import_master_verification_key, CommonClientImportMasterVerificationKeyArgs,
};
pub(crate) async fn execute(
args: CommonClientImportMasterVerificationKeyArgs,
) -> Result<(), ClientError> {
import_master_verification_key::<CliNativeClient, _>(args).await?;
println!("successfully imported master verification key!");
Ok(())
}
-59
View File
@@ -1,59 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs;
use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs;
use std::error::Error;
pub(crate) mod import_coin_index_signatures;
pub(crate) mod import_credential;
pub(crate) mod import_expiration_date_signatures;
pub(crate) mod import_master_verification_key;
pub(crate) mod show_ticketbooks;
#[derive(Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Ecash {
#[clap(subcommand)]
pub command: EcashCommands,
}
impl Ecash {
pub async fn execute(self) -> Result<(), Box<dyn Error + Send + Sync>> {
match self.command {
EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?,
EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?,
EcashCommands::ImportCoinIndexSignatures(args) => {
import_coin_index_signatures::execute(args).await?
}
EcashCommands::ImportExpirationDateSignatures(args) => {
import_expiration_date_signatures::execute(args).await?
}
EcashCommands::ImportMasterVerificationKey(args) => {
import_master_verification_key::execute(args).await?
}
}
Ok(())
}
}
#[derive(Subcommand)]
pub enum EcashCommands {
/// Display information associated with the imported ticketbooks,
ShowTicketBooks(show_ticketbooks::Args),
/// Import a pre-generated ticketbook
ImportTicketBook(CommonClientImportTicketBookArgs),
/// Import coin index signatures needed for ticketbooks
ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs),
/// Import expiration date signatures needed for ticketbooks
ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs),
/// Import master verification key needed for ticketbooks
ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs),
}
@@ -1,32 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::cli_helpers::client_show_ticketbooks::{
show_ticketbooks, CommonShowTicketbooksArgs,
};
#[derive(clap::Args)]
pub(crate) struct Args {
#[command(flatten)]
common_args: CommonShowTicketbooksArgs,
#[arg(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
impl AsRef<CommonShowTicketbooksArgs> for Args {
fn as_ref(&self) -> &CommonShowTicketbooksArgs {
&self.common_args
}
}
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
let output = args.output;
let res = show_ticketbooks::<CliNativeClient, _>(args).await?;
println!("{}", output.format(&res));
Ok(())
}
@@ -4,10 +4,10 @@
use crate::commands::CliNativeClient; use crate::commands::CliNativeClient;
use crate::error::ClientError; use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_credential::{ use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportTicketBookArgs, import_credential, CommonClientImportCredentialArgs,
}; };
pub(crate) async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), ClientError> { pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> {
import_credential::<CliNativeClient, _>(args).await?; import_credential::<CliNativeClient, _>(args).await?;
println!("successfully imported credential!"); println!("successfully imported credential!");
Ok(()) Ok(())
+5 -5
View File
@@ -6,13 +6,13 @@ use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2; use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::old_config_v1_1_33::ConfigV1_1_33; use crate::client::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::client::config::{BaseClientConfig, Config}; use crate::client::config::{BaseClientConfig, Config};
use crate::commands::ecash::Ecash;
use crate::error::ClientError; use crate::error::ClientError;
use clap::CommandFactory; use clap::CommandFactory;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use log::{error, info}; use log::{error, info};
use nym_bin_common::bin_info; use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient; use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33; use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_config::OptionalSet; use nym_config::OptionalSet;
@@ -22,7 +22,7 @@ use std::sync::OnceLock;
mod add_gateway; mod add_gateway;
pub(crate) mod build_info; pub(crate) mod build_info;
pub(crate) mod ecash; pub(crate) mod import_credential;
pub(crate) mod init; pub(crate) mod init;
mod list_gateways; mod list_gateways;
pub(crate) mod run; pub(crate) mod run;
@@ -72,8 +72,8 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters /// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run), Run(run::Run),
/// Ecash-related functionalities /// Import a pre-generated credential
Ecash(Ecash), ImportCredential(CommonClientImportCredentialArgs),
/// List all registered with gateways /// List all registered with gateways
ListGateways(list_gateways::Args), ListGateways(list_gateways::Args),
@@ -112,7 +112,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command { match args.command {
Commands::Init(m) => init::execute(m).await?, Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?, Commands::Run(m) => run::execute(m).await?,
Commands::Ecash(ecash) => ecash.execute().await?, Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
+1 -1
View File
@@ -422,7 +422,7 @@ impl Handler {
) { ) {
// We don't want a crash in the connection handler to trigger a shutdown of the whole // We don't want a crash in the connection handler to trigger a shutdown of the whole
// process. // process.
task_client.disarm(); task_client.mark_as_success();
let ws_stream = match accept_async(socket).await { let ws_stream = match accept_async(socket).await {
Ok(ws_stream) => ws_stream, Ok(ws_stream) => ws_stream,
+6 -18
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.40" version = "1.1.37"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
@@ -11,9 +11,7 @@ license.workspace = true
bs58 = { workspace = true } bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] } clap = { workspace = true, features = ["cargo", "derive"] }
log = { workspace = true } log = { workspace = true }
serde = { workspace = true, features = [ serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
"derive",
] } # for config serialization/deserialization
serde_json = { workspace = true } serde_json = { workspace = true }
tap = { workspace = true } tap = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
@@ -24,21 +22,13 @@ url = { workspace = true }
zeroize = { workspace = true } zeroize = { workspace = true }
# internal # internal
nym-bin-common = { path = "../../common/bin-common", features = [ nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
"output_format", nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
"clap",
] }
nym-client-core = { path = "../../common/client-core", features = [
"fs-credentials-storage",
"fs-surb-storage",
"fs-gateways-storage",
"cli",
] }
nym-config = { path = "../../common/config" } nym-config = { path = "../../common/config" }
nym-credential-storage = { path = "../../common/credential-storage" } nym-credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" } nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" } nym-crypto = { path = "../../common/crypto" }
nym-gateway-requests = { path = "../../common/gateway-requests" } nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-id = { path = "../../common/nym-id" } nym-id = { path = "../../common/nym-id" }
nym-network-defaults = { path = "../../common/network-defaults" } nym-network-defaults = { path = "../../common/network-defaults" }
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" } nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
@@ -46,9 +36,7 @@ nym-pemstore = { path = "../../common/pemstore" }
nym-socks5-client-core = { path = "../../common/socks5-client-core" } nym-socks5-client-core = { path = "../../common/socks5-client-core" }
nym-sphinx = { path = "../../common/nymsphinx" } nym-sphinx = { path = "../../common/nymsphinx" }
nym-topology = { path = "../../common/topology" } nym-topology = { path = "../../common/topology" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
"http-client",
] }
[features] [features]
default = [] default = []
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_coin_index_signatures::{
import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportCoinIndexSignaturesArgs,
) -> Result<(), Socks5ClientError> {
import_coin_index_signatures::<CliSocks5Client, _>(args).await?;
println!("successfully imported coin index signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{
import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportExpirationDateSignaturesArgs,
) -> Result<(), Socks5ClientError> {
import_expiration_date_signatures::<CliSocks5Client, _>(args).await?;
println!("successfully imported expiration date signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_master_verification_key::{
import_master_verification_key, CommonClientImportMasterVerificationKeyArgs,
};
pub(crate) async fn execute(
args: CommonClientImportMasterVerificationKeyArgs,
) -> Result<(), Socks5ClientError> {
import_master_verification_key::<CliSocks5Client, _>(args).await?;
println!("successfully imported master verification key!");
Ok(())
}
-59
View File
@@ -1,59 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs;
use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs;
use std::error::Error;
pub(crate) mod import_coin_index_signatures;
pub(crate) mod import_credential;
pub(crate) mod import_expiration_date_signatures;
pub(crate) mod import_master_verification_key;
pub(crate) mod show_ticketbooks;
#[derive(Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Ecash {
#[clap(subcommand)]
pub command: EcashCommands,
}
impl Ecash {
pub async fn execute(self) -> Result<(), Box<dyn Error + Send + Sync>> {
match self.command {
EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?,
EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?,
EcashCommands::ImportCoinIndexSignatures(args) => {
import_coin_index_signatures::execute(args).await?
}
EcashCommands::ImportExpirationDateSignatures(args) => {
import_expiration_date_signatures::execute(args).await?
}
EcashCommands::ImportMasterVerificationKey(args) => {
import_master_verification_key::execute(args).await?
}
}
Ok(())
}
}
#[derive(Subcommand)]
pub enum EcashCommands {
/// Display information associated with the imported ticketbooks,
ShowTicketBooks(show_ticketbooks::Args),
/// Import a pre-generated ticketbook
ImportTicketBook(CommonClientImportTicketBookArgs),
/// Import coin index signatures needed for ticketbooks
ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs),
/// Import expiration date signatures needed for ticketbooks
ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs),
/// Import master verification key needed for ticketbooks
ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs),
}
@@ -1,32 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::cli_helpers::client_show_ticketbooks::{
show_ticketbooks, CommonShowTicketbooksArgs,
};
#[derive(clap::Args)]
pub struct Args {
#[command(flatten)]
common_args: CommonShowTicketbooksArgs,
#[arg(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
impl AsRef<CommonShowTicketbooksArgs> for Args {
fn as_ref(&self) -> &CommonShowTicketbooksArgs {
&self.common_args
}
}
pub async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let output = args.output;
let res = show_ticketbooks::<CliSocks5Client, _>(args).await?;
println!("{}", output.format(&res));
Ok(())
}
@@ -4,10 +4,12 @@
use crate::commands::CliSocks5Client; use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError; use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_credential::{ use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportTicketBookArgs, import_credential, CommonClientImportCredentialArgs,
}; };
pub async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), Socks5ClientError> { pub(crate) async fn execute(
args: CommonClientImportCredentialArgs,
) -> Result<(), Socks5ClientError> {
import_credential::<CliSocks5Client, _>(args).await?; import_credential::<CliSocks5Client, _>(args).await?;
println!("successfully imported credential!"); println!("successfully imported credential!");
Ok(()) Ok(())
+5 -5
View File
@@ -1,7 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::commands::ecash::Ecash;
use crate::config::old_config_v1_1_13::OldConfigV1_1_13; use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_20::ConfigV1_1_20; use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2; use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
@@ -14,6 +13,7 @@ use clap::{Parser, Subcommand};
use log::{error, info}; use log::{error, info};
use nym_bin_common::bin_info; use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient; use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33; use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
@@ -26,7 +26,7 @@ use std::sync::OnceLock;
mod add_gateway; mod add_gateway;
pub(crate) mod build_info; pub(crate) mod build_info;
pub mod ecash; mod import_credential;
pub mod init; pub mod init;
mod list_gateways; mod list_gateways;
pub(crate) mod run; pub(crate) mod run;
@@ -76,8 +76,8 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters /// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run), Run(run::Run),
/// Ecash-related functionalities /// Import a pre-generated credential
Ecash(Ecash), ImportCredential(CommonClientImportCredentialArgs),
/// List all registered with gateways /// List all registered with gateways
ListGateways(list_gateways::Args), ListGateways(list_gateways::Args),
@@ -119,7 +119,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command { match args.command {
Commands::Init(m) => init::execute(m).await?, Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?, Commands::Run(m) => run::execute(m).await?,
Commands::Ecash(ecash) => ecash.execute().await?, Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::Recipient; use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::{GatewayClient, InitMessage, PeerPublicKey}; use nym_wireguard_types::{GatewayClient, InitMessage};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer; use crate::make_bincode_serializer;
@@ -57,19 +57,6 @@ impl AuthenticatorRequest {
) )
} }
pub fn new_query_request(peer_public_key: PeerPublicKey, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random();
(
Self {
version: VERSION,
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
reply_to,
request_id,
},
request_id,
)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> { pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
use bincode::Options; use bincode::Options;
make_bincode_serializer().serialize(self) make_bincode_serializer().serialize(self)
@@ -80,5 +67,4 @@ impl AuthenticatorRequest {
pub enum AuthenticatorRequestData { pub enum AuthenticatorRequestData {
Initial(InitMessage), Initial(InitMessage),
Final(GatewayClient), Final(GatewayClient),
QueryBandwidth(PeerPublicKey),
} }
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::Recipient; use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::registration::{RegistrationData, RegistredData, RemainingBandwidthData}; use nym_wireguard_types::registration::RegistrationData;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer; use crate::make_bincode_serializer;
@@ -33,31 +33,10 @@ impl AuthenticatorResponse {
} }
} }
pub fn new_registered( pub fn new_registered(reply_to: Recipient, request_id: u64) -> Self {
registred_data: RegistredData,
reply_to: Recipient,
request_id: u64,
) -> Self {
Self { Self {
version: VERSION, version: VERSION,
data: AuthenticatorResponseData::Registered(RegisteredResponse { data: AuthenticatorResponseData::Registered(RegisteredResponse {
reply: registred_data,
reply_to,
request_id,
}),
reply_to,
}
}
pub fn new_remaining_bandwidth(
remaining_bandwidth_data: Option<RemainingBandwidthData>,
reply_to: Recipient,
request_id: u64,
) -> Self {
Self {
version: VERSION,
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
reply: remaining_bandwidth_data,
reply_to, reply_to,
request_id, request_id,
}), }),
@@ -85,7 +64,6 @@ impl AuthenticatorResponse {
match &self.data { match &self.data {
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id), AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
AuthenticatorResponseData::Registered(response) => Some(response.request_id), AuthenticatorResponseData::Registered(response) => Some(response.request_id),
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
} }
} }
} }
@@ -94,7 +72,6 @@ impl AuthenticatorResponse {
pub enum AuthenticatorResponseData { pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse), PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse), Registered(RegisteredResponse),
RemainingBandwidth(RemainingBandwidthResponse),
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -108,12 +85,4 @@ pub struct PendingRegistrationResponse {
pub struct RegisteredResponse { pub struct RegisteredResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistredData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>,
} }
+1 -2
View File
@@ -14,14 +14,13 @@ thiserror = { workspace = true }
url = { workspace = true } url = { workspace = true }
zeroize = { workspace = true } zeroize = { workspace = true }
nym-ecash-time = { path = "../ecash-time" } nym-coconut = { path = "../nymcoconut" }
nym-credential-storage = { path = "../credential-storage" } nym-credential-storage = { path = "../credential-storage" }
nym-credentials = { path = "../credentials" } nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] } nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
nym-network-defaults = { path = "../network-defaults" } nym-network-defaults = { path = "../network-defaults" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
path = "../client-libs/validator-client" path = "../client-libs/validator-client"
+50 -95
View File
@@ -1,132 +1,87 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError; use crate::error::BandwidthControllerError;
use crate::utils::{ use nym_credential_storage::models::StorableIssuedCredential;
get_aggregate_verification_key, get_coin_index_signatures, get_expiration_date_signatures,
};
use log::info;
use nym_credential_storage::storage::Storage; use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::IssuanceTicketBook; use nym_credentials::coconut::bandwidth::{CredentialType, IssuanceBandwidthCredential};
use nym_credentials::ecash::utils::obtain_aggregate_wallet; use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_credentials::IssuedTicketBook; use nym_crypto::asymmetric::{encryption, identity};
use nym_credentials_interface::TicketType; use nym_validator_client::coconut::all_coconut_api_clients;
use nym_crypto::asymmetric::identity; use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient;
use nym_ecash_time::{ecash_default_expiration_date, Date}; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::coconut::all_ecash_api_clients; use nym_validator_client::nyxd::Coin;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::EcashSigningClient;
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient};
use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
use nym_validator_client::EcashApiClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use state::State;
use zeroize::Zeroizing;
pub async fn make_deposit<C>( pub mod state;
client: &C,
client_id: &[u8], pub async fn deposit<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError>
expiration: Option<Date>,
ticketbook_type: TicketType,
) -> Result<IssuanceTicketBook, BandwidthControllerError>
where where
C: EcashSigningClient + EcashQueryClient + Sync, C: CoconutBandwidthSigningClient + Sync,
{ {
let mut rng = OsRng; let mut rng = OsRng;
let signing_key = identity::PrivateKey::new(&mut rng); let signing_key = identity::PrivateKey::new(&mut rng);
let expiration = expiration.unwrap_or_else(ecash_default_expiration_date); let encryption_key = encryption::PrivateKey::new(&mut rng);
let deposit_amount = client.get_required_deposit_amount().await?; let tx_hash = client
info!("we'll need to deposit {deposit_amount} to obtain the ticketbook"); .deposit(
let result = client amount.clone(),
.make_ticketbook_deposit( CredentialType::Voucher.to_string(),
signing_key.public_key().to_base58_string(), signing_key.public_key().to_base58_string(),
deposit_amount.into(), encryption_key.public_key().to_base58_string(),
None, None,
) )
.await?; .await?
.transaction_hash;
let deposit_id = result.parse_singleton_u32_contract_data()?; let voucher =
IssuanceBandwidthCredential::new_voucher(amount, tx_hash, signing_key, encryption_key);
info!("our ticketbook deposit has been stored under id {deposit_id}"); let state = State { voucher };
Ok(IssuanceTicketBook::new_with_expiration( Ok(state)
deposit_id,
client_id,
signing_key,
ticketbook_type,
expiration,
))
} }
pub async fn query_and_persist_required_global_data<S>( pub async fn get_bandwidth_voucher<C, St>(
storage: &S, state: &State,
epoch_id: EpochId,
expiration_date: Date,
apis: Vec<EcashApiClient>,
) -> Result<(), BandwidthControllerError>
where
S: Storage,
<S as Storage>::StorageError: Send + Sync + 'static,
{
log::info!("Getting master verification key");
// this will also persist the key in the storage if was not there already
get_aggregate_verification_key(storage, epoch_id, apis.clone()).await?;
log::info!("Getting expiration date signatures");
// this will also persist the signatures in the storage if they were not there already
get_expiration_date_signatures(storage, epoch_id, expiration_date, apis.clone()).await?;
log::info!("Getting coin indices signatures");
// this will also persist the signatures in the storage if they were not there already
get_coin_index_signatures(storage, epoch_id, apis).await?;
Ok(())
}
pub async fn get_ticket_book<C, St>(
issuance_data: &IssuanceTicketBook,
client: &C, client: &C,
storage: &St, storage: &St,
apis: Option<Vec<EcashApiClient>>, ) -> Result<(), BandwidthControllerError>
) -> Result<IssuedTicketBook, BandwidthControllerError>
where where
C: DkgQueryClient + Send + Sync, C: DkgQueryClient + Send + Sync,
St: Storage, St: Storage,
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
// temporary
assert!(state.voucher.typ().is_voucher());
let epoch_id = client.get_current_epoch().await?.epoch_id; let epoch_id = client.get_current_epoch().await?.epoch_id;
let threshold = client let threshold = client
.get_current_epoch_threshold() .get_current_epoch_threshold()
.await? .await?
.ok_or(BandwidthControllerError::NoThreshold)?; .ok_or(BandwidthControllerError::NoThreshold)?;
let apis = match apis { let coconut_api_clients = all_coconut_api_clients(client, epoch_id).await?;
Some(apis) => apis,
None => all_ecash_api_clients(client, epoch_id).await?, let signature =
obtain_aggregate_signature(&state.voucher, &coconut_api_clients, threshold).await?;
let issued = state.voucher.to_issued_credential(signature, epoch_id);
// make sure the data gets zeroized after persisting it
let credential_data = Zeroizing::new(issued.pack_v1());
let storable = StorableIssuedCredential {
serialization_revision: issued.current_serialization_revision(),
credential_data: credential_data.as_ref(),
credential_type: issued.typ().to_string(),
epoch_id: epoch_id
.try_into()
.expect("our epoch is has run over u32::MAX!"),
}; };
log::info!("Querying wallet signatures");
let wallet = obtain_aggregate_wallet(issuance_data, &apis, threshold).await?;
info!("managed to obtain sufficient number of partial signatures!");
log::info!("Getting expiration date signatures");
// this will also persist the signatures in the storage if they were not there already
get_expiration_date_signatures(
storage,
epoch_id,
issuance_data.expiration_date(),
apis.clone(),
)
.await?;
log::info!("Getting coin indices signatures");
// this will also persist the signatures in the storage if they were not there already
get_coin_index_signatures(storage, epoch_id, apis).await?;
let issued = issuance_data.to_issued_ticketbook(wallet, epoch_id);
info!("persisting the ticketbook into the storage...");
storage storage
.insert_issued_ticketbook(&issued) .insert_issued_credential(storable)
.await .await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?; .map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))
Ok(issued)
} }
@@ -0,0 +1,14 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials::coconut::bandwidth::IssuanceBandwidthCredential;
pub struct State {
pub voucher: IssuanceBandwidthCredential,
}
impl State {
pub fn new(voucher: IssuanceBandwidthCredential) -> Self {
State { voucher }
}
}
+5 -16
View File
@@ -1,12 +1,12 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_coconut::CoconutError;
use nym_credential_storage::error::StorageError; use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialsError; use nym_credentials::error::Error as CredentialsError;
use nym_credentials_interface::CompactEcashError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError; use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_validator_client::coconut::EcashApiError; use nym_validator_client::coconut::CoconutApiError;
use nym_validator_client::error::ValidatorClientError; use nym_validator_client::error::ValidatorClientError;
use thiserror::Error; use thiserror::Error;
@@ -16,7 +16,7 @@ pub enum BandwidthControllerError {
Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError), Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError),
#[error("coconut api query failure: {0}")] #[error("coconut api query failure: {0}")]
CoconutApiError(#[from] EcashApiError), CoconutApiError(#[from] CoconutApiError),
#[error("There was a credential storage error - {0}")] #[error("There was a credential storage error - {0}")]
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>), CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
@@ -28,8 +28,8 @@ pub enum BandwidthControllerError {
#[error(transparent)] #[error(transparent)]
StorageError(#[from] StorageError), StorageError(#[from] StorageError),
#[error("Ecash error - {0}")] #[error("Coconut error - {0}")]
EcashError(#[from] CompactEcashError), CoconutError(#[from] CoconutError),
#[error("Validator client error - {0}")] #[error("Validator client error - {0}")]
ValidatorError(#[from] ValidatorClientError), ValidatorError(#[from] ValidatorClientError),
@@ -51,15 +51,4 @@ pub enum BandwidthControllerError {
#[error("can't handle recovering storage with revision {stored}. {expected} was expected")] #[error("can't handle recovering storage with revision {stored}. {expected} was expected")]
UnsupportedCredentialStorageRevision { stored: u8, expected: u8 }, UnsupportedCredentialStorageRevision { stored: u8, expected: u8 },
#[error("did not receive a valid response for aggregated data ({typ}) from ANY nym-api")]
ExhaustedApiQueries { typ: String },
}
impl BandwidthControllerError {
pub fn credential_storage_error(
source: impl std::error::Error + Send + Sync + 'static,
) -> Self {
BandwidthControllerError::CredentialStorageError(Box::new(source))
}
} }
+90 -151
View File
@@ -1,24 +1,16 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
#![warn(clippy::todo)]
#![warn(clippy::dbg_macro)]
use crate::error::BandwidthControllerError; use crate::error::BandwidthControllerError;
use crate::utils::{ use crate::utils::stored_credential_to_issued_bandwidth;
get_aggregate_verification_key, get_coin_index_signatures, get_expiration_date_signatures, use log::{debug, error, warn};
ApiClientsWrapper,
};
use log::error;
use nym_credential_storage::models::RetrievedTicketbook;
use nym_credential_storage::storage::Storage; use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::CredentialSpendingData; use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials_interface::{ use nym_credentials::coconut::bandwidth::CredentialSpendingData;
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth, use nym_credentials::coconut::utils::obtain_aggregate_verification_key;
}; use nym_credentials::IssuedBandwidthCredential;
use nym_ecash_time::Date; use nym_credentials_interface::VerificationKey;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
@@ -43,20 +35,13 @@ pub struct PreparedCredential {
/// could use correct verification key for validation. /// could use correct verification key for validation.
pub epoch_id: EpochId, pub epoch_id: EpochId,
/// Auxiliary metadata associated with the withdrawn credential /// The database id of the stored credential.
pub metadata: PreparedCredentialMetadata, pub credential_id: i64,
} }
#[derive(Copy, Clone)] pub struct RetrievedCredential {
pub struct PreparedCredentialMetadata { pub credential: IssuedBandwidthCredential,
/// The database id of the stored credential. pub credential_id: i64,
pub ticketbook_id: i64,
/// The number of tickets withdrawn in this credential
pub tickets_withdrawn: u32,
/// The amount of tickets used INCLUDING those tickets that JUST got withdrawn
pub used_tickets: u32,
} }
impl<C, St: Storage> BandwidthController<C, St> { impl<C, St: Storage> BandwidthController<C, St> {
@@ -65,157 +50,111 @@ impl<C, St: Storage> BandwidthController<C, St> {
} }
/// Tries to retrieve one of the stored, unused credentials that hasn't yet expired. /// Tries to retrieve one of the stored, unused credentials that hasn't yet expired.
pub async fn get_next_usable_ticketbook( /// It marks any retrieved intermediate credentials as expired.
pub async fn get_next_usable_credential(
&self, &self,
tickets: u32, gateway_id: &str,
) -> Result<RetrievedTicketbook, BandwidthControllerError> ) -> Result<RetrievedCredential, BandwidthControllerError>
where where
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
let Some(ticketbook) = self loop {
.storage let Some(maybe_next) = self
.get_next_unspent_usable_ticketbook(tickets) .storage
.await .get_next_unspent_credential(gateway_id)
.map_err(BandwidthControllerError::credential_storage_error)? .await
else { .map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?
return Err(BandwidthControllerError::NoCredentialsAvailable); else {
}; return Err(BandwidthControllerError::NoCredentialsAvailable);
};
let id = maybe_next.id;
Ok(ticketbook) // try to deserialize it
let valid_credential = match stored_credential_to_issued_bandwidth(maybe_next) {
// check if it has already expired
Ok(credential) => match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(_) => {
debug!("credential {id} is a bandwidth voucher");
credential
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
debug!("credential {id} is a free pass");
if freepass_info.expired() {
warn!("the free pass (id: {id}) has already expired! The expiration was set to {}", freepass_info.expiry_date());
self.storage.mark_expired(id).await.map_err(|err| {
BandwidthControllerError::CredentialStorageError(Box::new(err))
})?;
continue;
}
credential
}
},
Err(err) => {
error!("failed to deserialize credential with id {id}: {err}. it may need to be manually removed from the storage");
return Err(err);
}
};
return Ok(RetrievedCredential {
credential: valid_credential,
credential_id: id,
});
}
} }
pub async fn attempt_revert_ticket_usage( pub fn storage(&self) -> &St {
&self, &self.storage
info: PreparedCredentialMetadata,
) -> Result<bool, BandwidthControllerError>
where
<St as Storage>::StorageError: Send + Sync + 'static,
{
self.storage
.attempt_revert_ticketbook_withdrawal(
info.ticketbook_id,
info.used_tickets,
info.tickets_withdrawn,
)
.await
.map_err(BandwidthControllerError::credential_storage_error)
} }
async fn get_aggregate_verification_key( async fn get_aggregate_verification_key(
&self, &self,
epoch_id: EpochId, epoch_id: EpochId,
apis: &mut ApiClientsWrapper, ) -> Result<VerificationKey, BandwidthControllerError>
) -> Result<VerificationKeyAuth, BandwidthControllerError>
where where
C: DkgQueryClient + Sync + Send, C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
let ecash_apis = apis.get_or_init(epoch_id, &self.client).await?; let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?;
get_aggregate_verification_key(&self.storage, epoch_id, ecash_apis).await Ok(obtain_aggregate_verification_key(&coconut_api_clients)?)
} }
async fn get_coin_index_signatures( pub async fn prepare_bandwidth_credential(
&self, &self,
epoch_id: EpochId, gateway_id: &str,
apis: &mut ApiClientsWrapper,
) -> Result<Vec<AnnotatedCoinIndexSignature>, BandwidthControllerError>
where
C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static,
{
let ecash_apis = apis.get_or_init(epoch_id, &self.client).await?;
get_coin_index_signatures(&self.storage, epoch_id, ecash_apis).await
}
async fn get_expiration_date_signatures(
&self,
epoch_id: EpochId,
expiration_date: Date,
apis: &mut ApiClientsWrapper,
) -> Result<Vec<AnnotatedExpirationDateSignature>, BandwidthControllerError>
where
C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static,
{
let ecash_apis = apis.get_or_init(epoch_id, &self.client).await?;
get_expiration_date_signatures(&self.storage, epoch_id, expiration_date, ecash_apis).await
}
async fn prepare_ecash_ticket_inner(
&self,
provider_pk: [u8; 32],
tickets_to_spend: u32,
mut retrieved_ticketbook: RetrievedTicketbook,
) -> Result<CredentialSpendingData, BandwidthControllerError>
where
C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static,
{
let epoch_id = retrieved_ticketbook.ticketbook.epoch_id();
let expiration_date = retrieved_ticketbook.ticketbook.expiration_date();
let mut api_clients = Default::default();
let verification_key = self
.get_aggregate_verification_key(epoch_id, &mut api_clients)
.await?;
let expiration_signatures = self
.get_expiration_date_signatures(epoch_id, expiration_date, &mut api_clients)
.await?;
let coin_indices_signatures = self
.get_coin_index_signatures(epoch_id, &mut api_clients)
.await?;
let pay_info = retrieved_ticketbook
.ticketbook
.generate_pay_info(provider_pk);
let spend_request = retrieved_ticketbook.ticketbook.prepare_for_spending(
&verification_key,
pay_info.into(),
&coin_indices_signatures,
&expiration_signatures,
tickets_to_spend as u64,
)?;
Ok(spend_request)
}
pub async fn prepare_ecash_ticket(
&self,
provider_pk: [u8; 32],
tickets_to_spend: u32,
) -> Result<PreparedCredential, BandwidthControllerError> ) -> Result<PreparedCredential, BandwidthControllerError>
where where
C: DkgQueryClient + Sync + Send, C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
let retrieved_ticketbook = self.get_next_usable_ticketbook(tickets_to_spend).await?; let retrieved_credential = self.get_next_usable_credential(gateway_id).await?;
let ticketbook_id = retrieved_ticketbook.ticketbook_id; let epoch_id = retrieved_credential.credential.epoch_id();
let epoch_id = retrieved_ticketbook.ticketbook.epoch_id(); let credential_id = retrieved_credential.credential_id;
let used_tickets = let verification_key = self.get_aggregate_verification_key(epoch_id).await?;
retrieved_ticketbook.ticketbook.spent_tickets() as u32 + tickets_to_spend;
let metadata = PreparedCredentialMetadata {
ticketbook_id,
tickets_withdrawn: tickets_to_spend,
used_tickets,
};
match self let spend_request = retrieved_credential
.prepare_ecash_ticket_inner(provider_pk, tickets_to_spend, retrieved_ticketbook) .credential
.prepare_for_spending(&verification_key)?;
Ok(PreparedCredential {
data: spend_request,
epoch_id,
credential_id,
})
}
pub async fn consume_credential(
&self,
id: i64,
gateway_id: &str,
) -> Result<(), BandwidthControllerError>
where
<St as Storage>::StorageError: Send + Sync + 'static,
{
self.storage
.consume_coconut_credential(id, gateway_id)
.await .await
{ .map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))
Ok(data) => Ok(PreparedCredential {
data,
epoch_id,
metadata,
}),
Err(err) => {
error!("failed to prepare credential spending request. attempting to revert withdrawal...");
self.attempt_revert_ticket_usage(metadata).await?;
Err(err)
}
}
} }
} }
+15 -194
View File
@@ -2,200 +2,21 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError; use crate::error::BandwidthControllerError;
use log::warn; use nym_credential_storage::models::StoredIssuedCredential;
use nym_credential_storage::storage::Storage; use nym_credentials::coconut::bandwidth::issued::CURRENT_SERIALIZATION_REVISION;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey; use nym_credentials::coconut::bandwidth::IssuedBandwidthCredential;
use nym_credentials::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use nym_credentials_interface::{
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth,
};
use nym_ecash_time::Date;
use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::EcashApiClient;
use rand::prelude::SliceRandom;
use rand::thread_rng;
use std::fmt::Display;
use std::future::Future;
// it really doesn't need the RwLock because it's never moved across tasks, pub fn stored_credential_to_issued_bandwidth(
// but we need all the Send/Sync action cred: StoredIssuedCredential,
#[derive(Default)] ) -> Result<IssuedBandwidthCredential, BandwidthControllerError> {
pub(crate) struct ApiClientsWrapper(Option<Vec<EcashApiClient>>); if cred.serialization_revision != CURRENT_SERIALIZATION_REVISION {
return Err(
impl ApiClientsWrapper { BandwidthControllerError::UnsupportedCredentialStorageRevision {
pub(crate) async fn get_or_init<C>( stored: cred.serialization_revision,
&mut self, expected: CURRENT_SERIALIZATION_REVISION,
epoch_id: EpochId, },
dkg_client: &C, );
) -> Result<Vec<EcashApiClient>, BandwidthControllerError>
where
C: DkgQueryClient + Sync + Send,
{
if let Some(cached) = &self.0 {
return Ok(cached.clone());
}
let clients = all_ecash_api_clients(dkg_client, epoch_id).await?;
// technically we don't have to be cloning all the clients here, but it's way simpler than
// dealing with locking and whatnot given the performance penalty is negligible
self.0 = Some(clients.clone());
Ok(clients)
} }
}
Ok(IssuedBandwidthCredential::unpack_v1(&cred.credential_data)?)
pub(crate) async fn query_random_apis_until_success<F, T, U, E>(
mut apis: Vec<EcashApiClient>,
f: F,
typ: impl Into<String>,
) -> Result<T, BandwidthControllerError>
where
F: Fn(EcashApiClient) -> U,
U: Future<Output = Result<T, E>>,
E: Display,
{
// try apis in pseudorandom way to remove any bias towards the first registered dealer
apis.shuffle(&mut thread_rng());
for api in apis {
let disp = api.to_string();
match f(api).await {
Ok(res) => return Ok(res),
Err(err) => {
warn!("failed to obtain valid response from API {disp}: {err}")
}
}
}
Err(BandwidthControllerError::ExhaustedApiQueries { typ: typ.into() })
}
pub(crate) async fn get_aggregate_verification_key<St>(
storage: &St,
epoch_id: EpochId,
ecash_apis: Vec<EcashApiClient>,
) -> Result<VerificationKeyAuth, BandwidthControllerError>
where
St: Storage,
<St as Storage>::StorageError: Send + Sync + 'static,
{
if let Some(stored) = storage
.get_master_verification_key(epoch_id)
.await
.map_err(BandwidthControllerError::credential_storage_error)?
{
return Ok(stored);
};
let master_vk = query_random_apis_until_success(
ecash_apis,
|api| async move { api.api_client.master_verification_key(Some(epoch_id)).await },
format!("aggregated verification key for epoch {epoch_id}"),
)
.await?
.key;
let full = EpochVerificationKey {
epoch_id,
key: master_vk,
};
// store the retrieved key
storage
.insert_master_verification_key(&full)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(full.key)
}
pub(crate) async fn get_coin_index_signatures<St>(
storage: &St,
epoch_id: EpochId,
ecash_apis: Vec<EcashApiClient>,
) -> Result<Vec<AnnotatedCoinIndexSignature>, BandwidthControllerError>
where
St: Storage,
<St as Storage>::StorageError: Send + Sync + 'static,
{
if let Some(stored) = storage
.get_coin_index_signatures(epoch_id)
.await
.map_err(BandwidthControllerError::credential_storage_error)?
{
return Ok(stored);
};
let index_sigs = query_random_apis_until_success(
ecash_apis,
|api| async move {
api.api_client
.global_coin_indices_signatures(Some(epoch_id))
.await
},
format!("aggregated coin index signatures for epoch {epoch_id}"),
)
.await?
.signatures;
let aggregated = AggregatedCoinIndicesSignatures {
epoch_id,
signatures: index_sigs,
};
// store the retrieved key
storage
.insert_coin_index_signatures(&aggregated)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(aggregated.signatures)
}
pub(crate) async fn get_expiration_date_signatures<St>(
storage: &St,
epoch_id: EpochId,
expiration_date: Date,
ecash_apis: Vec<EcashApiClient>,
) -> Result<Vec<AnnotatedExpirationDateSignature>, BandwidthControllerError>
where
St: Storage,
<St as Storage>::StorageError: Send + Sync + 'static,
{
if let Some(stored) = storage
.get_expiration_date_signatures(expiration_date)
.await
.map_err(BandwidthControllerError::credential_storage_error)?
{
return Ok(stored);
};
let expiration_sigs = query_random_apis_until_success(
ecash_apis,
|api| async move {
api.api_client
.global_expiration_date_signatures(Some(expiration_date))
.await
},
format!("aggregated coin index signatures for date {expiration_date}"),
)
.await?
.signatures;
let aggregated = AggregatedExpirationDateSignatures {
epoch_id,
expiration_date,
signatures: expiration_sigs,
};
// store the retrieved key
storage
.insert_expiration_date_signatures(&aggregated)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(aggregated.signatures)
} }
+5 -12
View File
@@ -14,12 +14,10 @@ base64 = { workspace = true }
bs58 = { workspace = true } bs58 = { workspace = true }
cfg-if = { workspace = true } cfg-if = { workspace = true }
clap = { workspace = true, optional = true } clap = { workspace = true, optional = true }
comfy-table = { version = "7.1.1", optional = true }
futures = { workspace = true } futures = { workspace = true }
humantime-serde = { workspace = true } humantime-serde = { workspace = true }
log = { workspace = true } log = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
rand_chacha = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
sha2 = { workspace = true } sha2 = { workspace = true }
@@ -39,7 +37,7 @@ nym-country-group = { path = "../country-group" }
nym-crypto = { path = "../crypto" } nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" } nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-gateway-client = { path = "../client-libs/gateway-client" } nym-gateway-client = { path = "../client-libs/gateway-client" }
nym-gateway-requests = { path = "../gateway-requests" } nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-metrics = { path = "../nym-metrics" } nym-metrics = { path = "../nym-metrics" }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" } nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" } nym-sphinx = { path = "../nymsphinx" }
@@ -47,15 +45,11 @@ nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["serializable"] } nym-topology = { path = "../topology", features = ["serializable"] }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" } nym-task = { path = "../task" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-credential-storage = { path = "../credential-storage" } nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" } nym-network-defaults = { path = "../network-defaults" }
nym-client-core-config-types = { path = "./config-types", features = [ nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"] }
"disk-persistence",
] }
nym-client-core-surb-storage = { path = "./surb-storage" } nym-client-core-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" } nym-client-core-gateways-storage = { path = "./gateways-storage" }
nym-ecash-time = { path = "../ecash-time" }
### For serving prometheus metrics ### For serving prometheus metrics
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper]
@@ -71,7 +65,7 @@ features = ["tokio"]
### ###
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.16" version = "0.1.11"
features = ["time"] features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
@@ -102,7 +96,7 @@ workspace = true
features = ["tokio"] features = ["tokio"]
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers] [target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.3.0" version = "0.2.4"
features = ["futures"] features = ["futures"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
@@ -118,8 +112,7 @@ tempfile = { workspace = true }
[features] [features]
default = [] default = []
cli = ["clap", "comfy-table"] cli = ["clap"]
fs-credentials-storage = ["nym-credential-storage/persistent-storage"]
fs-surb-storage = ["nym-client-core-surb-storage/fs-surb-storage"] fs-surb-storage = ["nym-client-core-surb-storage/fs-surb-storage"]
fs-gateways-storage = ["nym-client-core-gateways-storage/fs-gateways-storage"] fs-gateways-storage = ["nym-client-core-gateways-storage/fs-gateways-storage"]
wasm = ["nym-gateway-client/wasm"] wasm = ["nym-gateway-client/wasm"]
@@ -18,7 +18,7 @@ url.workspace = true
zeroize = { workspace = true, features = ["zeroize_derive"] } zeroize = { workspace = true, features = ["zeroize_derive"] }
nym-crypto = { path = "../../crypto", features = ["asymmetric"] } nym-crypto = { path = "../../crypto", features = ["asymmetric"] }
nym-gateway-requests = { path = "../../gateway-requests" } nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true workspace = true
@@ -27,12 +27,7 @@ optional = true
[build-dependencies] [build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [ sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
"runtime-tokio-rustls",
"sqlite",
"macros",
"migrate",
] }
[features] [features]
fs-gateways-storage = ["sqlx"] fs-gateways-storage = ["sqlx"]
@@ -1,17 +0,0 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
CREATE TABLE remote_gateway_details_temp
(
gateway_id_bs58 TEXT NOT NULL UNIQUE PRIMARY KEY REFERENCES registered_gateway (gateway_id_bs58),
derived_aes128_ctr_blake3_hmac_keys_bs58 TEXT NOT NULL,
gateway_owner_address TEXT,
gateway_listener TEXT NOT NULL
);
INSERT INTO remote_gateway_details_temp SELECT gateway_id_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58, gateway_owner_address, gateway_listener FROM remote_gateway_details;
DROP TABLE remote_gateway_details;
ALTER TABLE remote_gateway_details_temp RENAME TO remote_gateway_details;
@@ -155,13 +155,14 @@ impl StorageManager {
) -> Result<(), sqlx::Error> { ) -> Result<(), sqlx::Error> {
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58, gateway_owner_address, gateway_listener) INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58, gateway_owner_address, gateway_listener, wg_tun_address)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?, ?)
"#, "#,
remote.gateway_id_bs58, remote.gateway_id_bs58,
remote.derived_aes128_ctr_blake3_hmac_keys_bs58, remote.derived_aes128_ctr_blake3_hmac_keys_bs58,
remote.gateway_owner_address, remote.gateway_owner_address,
remote.gateway_listener, remote.gateway_listener,
remote.wg_tun_address,
) )
.execute(&self.connection_pool) .execute(&self.connection_pool)
.await?; .await?;
@@ -67,12 +67,14 @@ impl GatewayDetails {
derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>, derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>,
gateway_owner_address: Option<AccountId>, gateway_owner_address: Option<AccountId>,
gateway_listener: Url, gateway_listener: Url,
wg_tun_address: Option<Url>,
) -> Self { ) -> Self {
GatewayDetails::Remote(RemoteGatewayDetails { GatewayDetails::Remote(RemoteGatewayDetails {
gateway_id, gateway_id,
derived_aes128_ctr_blake3_hmac_keys, derived_aes128_ctr_blake3_hmac_keys,
gateway_owner_address, gateway_owner_address,
gateway_listener, gateway_listener,
wg_tun_address,
}) })
} }
@@ -170,6 +172,7 @@ pub struct RawRemoteGatewayDetails {
pub derived_aes128_ctr_blake3_hmac_keys_bs58: String, pub derived_aes128_ctr_blake3_hmac_keys_bs58: String,
pub gateway_owner_address: Option<String>, pub gateway_owner_address: Option<String>,
pub gateway_listener: String, pub gateway_listener: String,
pub wg_tun_address: Option<String>,
} }
impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails { impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
@@ -214,11 +217,24 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
} }
})?; })?;
let wg_tun_address = value
.wg_tun_address
.as_ref()
.map(|addr| {
Url::parse(addr).map_err(|source| BadGateway::MalformedListener {
gateway_id: value.gateway_id_bs58.clone(),
raw_listener: addr.clone(),
source,
})
})
.transpose()?;
Ok(RemoteGatewayDetails { Ok(RemoteGatewayDetails {
gateway_id, gateway_id,
derived_aes128_ctr_blake3_hmac_keys, derived_aes128_ctr_blake3_hmac_keys,
gateway_owner_address, gateway_owner_address,
gateway_listener, gateway_listener,
wg_tun_address,
}) })
} }
} }
@@ -232,6 +248,7 @@ impl<'a> From<&'a RemoteGatewayDetails> for RawRemoteGatewayDetails {
.to_base58_string(), .to_base58_string(),
gateway_owner_address: value.gateway_owner_address.as_ref().map(|o| o.to_string()), gateway_owner_address: value.gateway_owner_address.as_ref().map(|o| o.to_string()),
gateway_listener: value.gateway_listener.to_string(), gateway_listener: value.gateway_listener.to_string(),
wg_tun_address: value.wg_tun_address.as_ref().map(|addr| addr.to_string()),
} }
} }
} }
@@ -247,6 +264,8 @@ pub struct RemoteGatewayDetails {
pub gateway_owner_address: Option<AccountId>, pub gateway_owner_address: Option<AccountId>,
pub gateway_listener: Url, pub gateway_listener: Url,
pub wg_tun_address: Option<Url>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -133,6 +133,7 @@ where
let gateway_setup = GatewaySetup::New { let gateway_setup = GatewaySetup::New {
specification: selection_spec, specification: selection_spec,
available_gateways, available_gateways,
wg_tun_address: None,
}; };
let init_details = let init_details =
@@ -161,5 +162,6 @@ where
active: common_args.set_active, active: common_args.set_active,
typ: gateway_registration.details.typ().to_string(), typ: gateway_registration.details.typ().to_string(),
endpoint: Some(gateway_details.gateway_listener.clone()), endpoint: Some(gateway_details.gateway_listener.clone()),
wg_tun_address: gateway_details.wg_tun_address.clone(),
}) })
} }
@@ -1,68 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("sig_data").required(true)),
))
]
pub struct CommonClientImportCoinIndexSignaturesArgs {
/// Id of client that is going to import the signatures
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the signatures.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[cfg_attr(feature = "cli", clap(long, group = "sig_data"))]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_coin_index_signatures<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportCoinIndexSignaturesArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.signatures_path.unwrap())?
}
};
nym_id::import_coin_index_signatures(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -11,14 +11,9 @@ fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
} }
#[cfg_attr(feature = "cli", derive(clap::Args))] #[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli", #[cfg_attr(feature = "cli", clap(group(clap::ArgGroup::new("cred_data").required(true))))]
clap(
group(clap::ArgGroup::new("cred_data").required(true)),
group(clap::ArgGroup::new("type").required(true)),
))
]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CommonClientImportTicketBookArgs { pub struct CommonClientImportCredentialArgs {
/// Id of client that is going to import the credential /// Id of client that is going to import the credential
#[cfg_attr(feature = "cli", clap(long))] #[cfg_attr(feature = "cli", clap(long))]
pub id: String, pub id: String,
@@ -31,15 +26,6 @@ pub struct CommonClientImportTicketBookArgs {
#[cfg_attr(feature = "cli", clap(long, group = "cred_data"))] #[cfg_attr(feature = "cli", clap(long, group = "cred_data"))]
pub(crate) credential_path: Option<PathBuf>, pub(crate) credential_path: Option<PathBuf>,
/// Specifies whether we're attempting to import a standalone ticketbook (i.e. serialised `IssuedTicketBook`)
#[cfg_attr(feature = "cli", clap(long, group = "type"))]
pub(crate) standalone: bool,
/// Specifies whether we're attempting to import full ticketboot
/// (i.e. one that **might** contain required global signatures; that is serialised `ImportableTicketBook`)
#[cfg_attr(feature = "cli", clap(long, group = "type"))]
pub(crate) full: bool,
// currently hidden as there exists only a single serialization standard // currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))] #[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>, pub(crate) version: Option<u8>,
@@ -47,7 +33,7 @@ pub struct CommonClientImportTicketBookArgs {
pub async fn import_credential<C, A>(args: A) -> Result<(), C::Error> pub async fn import_credential<C, A>(args: A) -> Result<(), C::Error>
where where
A: Into<CommonClientImportTicketBookArgs>, A: Into<CommonClientImportCredentialArgs>,
C: CliClient, C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>, C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{ {
@@ -68,19 +54,6 @@ where
} }
}; };
if common_args.standalone { nym_id::import_credential(credentials_store, raw_credential, common_args.version).await?;
nym_id::import_standalone_ticketbook(
credentials_store,
raw_credential,
common_args.version,
)
.await?;
} else {
// sanity check; clap should have ensured it
assert!(common_args.full);
nym_id::import_full_ticketbook(credentials_store, raw_credential, common_args.version)
.await?;
}
Ok(()) Ok(())
} }
@@ -1,68 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("sig_data").required(true)),
))
]
pub struct CommonClientImportExpirationDateSignaturesArgs {
/// Id of client that is going to import the signatures
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the signatures.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[cfg_attr(feature = "cli", clap(long, group = "sig_data"))]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_expiration_date_signatures<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportExpirationDateSignaturesArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.signatures_path.unwrap())?
}
};
nym_id::import_expiration_date_signatures(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -1,68 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("key_data_group").required(true)),
))
]
pub struct CommonClientImportMasterVerificationKeyArgs {
/// Id of client that is going to import the key
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the key.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded key data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "key_data_group", value_parser = parse_encoded_key_data))]
pub(crate) key_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary key data
#[cfg_attr(feature = "cli", clap(long, group = "key_data_group"))]
pub(crate) key_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_master_verification_key<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportMasterVerificationKeyArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.key_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.key_path.unwrap())?
}
};
nym_id::import_master_verification_key(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -172,6 +172,7 @@ where
let gateway_setup = GatewaySetup::New { let gateway_setup = GatewaySetup::New {
specification: selection_spec, specification: selection_spec,
available_gateways, available_gateways,
wg_tun_address: None,
}; };
let init_details = let init_details =
@@ -57,6 +57,7 @@ where
active: active_gateway == Some(remote_details.gateway_id), active: active_gateway == Some(remote_details.gateway_id),
typ: GatewayType::Remote.to_string(), typ: GatewayType::Remote.to_string(),
endpoint: Some(remote_details.gateway_listener), endpoint: Some(remote_details.gateway_listener),
wg_tun_address: remote_details.wg_tun_address,
}), }),
GatewayDetails::Custom(_) => info.push(GatewayInfo { GatewayDetails::Custom(_) => info.push(GatewayInfo {
registration: gateway.registration_timestamp, registration: gateway.registration_timestamp,
@@ -64,6 +65,7 @@ where
active: active_gateway == Some(gateway.details.gateway_id()), active: active_gateway == Some(gateway.details.gateway_id()),
typ: gateway.details.typ().to_string(), typ: gateway.details.typ().to_string(),
endpoint: None, endpoint: None,
wg_tun_address: None,
}), }),
}; };
} }
@@ -1,140 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use crate::error::ClientCoreError;
use nym_credential_storage::models::BasicTicketbookInformation;
use nym_credential_storage::storage::Storage;
use nym_credentials_interface::TicketType;
use nym_ecash_time::ecash_today;
use serde::{Deserialize, Serialize};
use time::Date;
#[derive(Serialize, Deserialize)]
pub struct AvailableTicketbook {
pub id: i64,
pub typ: TicketType,
pub expiration: Date,
pub issued_tickets: u32,
pub claimed_tickets: u32,
pub ticket_size: u64,
}
impl AvailableTicketbook {
#[cfg(feature = "cli")]
fn table_row(&self) -> comfy_table::Row {
let ecash_today = ecash_today().date();
let issued = self.issued_tickets;
let si_issued = si_scale::helpers::bibytes2((issued as u64 * self.ticket_size) as f64);
let claimed = self.claimed_tickets;
let si_claimed = si_scale::helpers::bibytes2((claimed as u64 * self.ticket_size) as f64);
let remaining = issued - claimed;
let si_remaining =
si_scale::helpers::bibytes2((remaining as u64 * self.ticket_size) as f64);
let si_size = si_scale::helpers::bibytes2(self.ticket_size as f64);
let expiration = if self.expiration <= ecash_today {
comfy_table::Cell::new(format!("EXPIRED ON {}", self.expiration))
.fg(comfy_table::Color::Red)
.add_attribute(comfy_table::Attribute::Bold)
} else {
comfy_table::Cell::new(self.expiration.to_string())
};
vec![
comfy_table::Cell::new(self.id.to_string()),
comfy_table::Cell::new(self.typ),
expiration,
comfy_table::Cell::new(format!("{issued} ({si_issued})")),
comfy_table::Cell::new(format!("{claimed} ({si_claimed})")),
comfy_table::Cell::new(format!("{remaining} ({si_remaining})")),
comfy_table::Cell::new(si_size),
]
.into()
}
}
impl TryFrom<BasicTicketbookInformation> for AvailableTicketbook {
type Error = ClientCoreError;
fn try_from(value: BasicTicketbookInformation) -> Result<Self, Self::Error> {
let typ = value
.ticketbook_type
.parse()
.map_err(|_| ClientCoreError::UnknownTicketType)?;
Ok(AvailableTicketbook {
id: value.id,
typ,
expiration: value.expiration_date,
issued_tickets: value.total_tickets,
claimed_tickets: value.used_tickets,
ticket_size: typ.to_repr().bandwidth_value(),
})
}
}
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct AvailableTicketbooks(Vec<AvailableTicketbook>);
#[cfg(feature = "cli")]
impl std::fmt::Display for AvailableTicketbooks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut table = comfy_table::Table::new();
table.set_header(vec![
"id",
"type",
"expiration",
"issued tickets (bandwidth)",
"claimed tickets (bandwidth)",
"remaining tickets (bandwidth)",
"ticket size",
]);
for ticketbook in &self.0 {
table.add_row(ticketbook.table_row());
}
writeln!(f, "{table}")?;
Ok(())
}
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[derive(Debug, Clone)]
pub struct CommonShowTicketbooksArgs {
/// Id of client that is going to display the ticketbook information
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
}
pub async fn show_ticketbooks<C, A>(args: A) -> Result<AvailableTicketbooks, C::Error>
where
A: AsRef<CommonShowTicketbooksArgs>,
C: CliClient,
{
let common_args = args.as_ref();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let ticketbooks = credentials_store
.get_ticketbooks_info()
.await
.map_err(|err| ClientCoreError::CredentialStoreError {
source: Box::new(err),
})?;
Ok(AvailableTicketbooks(
ticketbooks
.into_iter()
.map(TryInto::<AvailableTicketbook>::try_into)
.collect::<Result<_, _>>()?,
))
}
@@ -2,14 +2,10 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod client_add_gateway; pub mod client_add_gateway;
pub mod client_import_coin_index_signatures;
pub mod client_import_credential; pub mod client_import_credential;
pub mod client_import_expiration_date_signatures;
pub mod client_import_master_verification_key;
pub mod client_init; pub mod client_init;
pub mod client_list_gateways; pub mod client_list_gateways;
pub mod client_run; pub mod client_run;
pub mod client_show_ticketbooks;
pub mod client_switch_gateway; pub mod client_switch_gateway;
pub mod traits; pub mod traits;
mod types; mod types;
@@ -15,6 +15,7 @@ pub struct GatewayInfo {
pub typ: String, pub typ: String,
pub endpoint: Option<Url>, pub endpoint: Option<Url>,
pub wg_tun_address: Option<Url>,
} }
impl Display for GatewayInfo { impl Display for GatewayInfo {
@@ -30,6 +31,10 @@ impl Display for GatewayInfo {
if let Some(endpoint) = &self.endpoint { if let Some(endpoint) = &self.endpoint {
write!(f, " endpoint: {endpoint}")?; write!(f, " endpoint: {endpoint}")?;
} }
if let Some(wg_tun_address) = &self.wg_tun_address {
write!(f, " wg tun address: {wg_tun_address}")?;
}
Ok(()) Ok(())
} }
} }
@@ -35,15 +35,15 @@ use crate::init::{
}; };
use crate::{config, spawn_future}; use crate::{config, spawn_future};
use futures::channel::mpsc; use futures::channel::mpsc;
use log::*; use log::{debug, error, info, warn};
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore}; use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_client::client::config::GatewayClientConfig;
use nym_gateway_client::{ use nym_gateway_client::{
AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter, AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter,
}; };
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, WG_TUN_DEVICE_ADDRESS};
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity; use nym_sphinx::addressing::nodes::NodeIdentity;
@@ -180,6 +180,7 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
dkg_query_client: Option<C>, dkg_query_client: Option<C>,
wait_for_gateway: bool, wait_for_gateway: bool,
wireguard_connection: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>, custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>, custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
shutdown: Option<TaskClient>, shutdown: Option<TaskClient>,
@@ -203,6 +204,7 @@ where
client_store, client_store,
dkg_query_client, dkg_query_client,
wait_for_gateway: false, wait_for_gateway: false,
wireguard_connection: false,
custom_topology_provider: None, custom_topology_provider: None,
custom_gateway_transceiver: None, custom_gateway_transceiver: None,
shutdown: None, shutdown: None,
@@ -223,6 +225,12 @@ where
self self
} }
#[must_use]
pub fn with_wireguard_connection(mut self, wireguard_connection: bool) -> Self {
self.wireguard_connection = wireguard_connection;
self
}
#[must_use] #[must_use]
pub fn with_topology_provider( pub fn with_topology_provider(
mut self, mut self,
@@ -352,6 +360,7 @@ where
async fn start_gateway_client( async fn start_gateway_client(
config: &Config, config: &Config,
wireguard_connection: bool,
initialisation_result: InitialisationResult, initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>, bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter, packet_router: PacketRouter,
@@ -367,33 +376,44 @@ where
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails); return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails);
}; };
let mut gateway_client = let mut gateway_client = if let Some(existing_client) =
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client { initialisation_result.authenticated_ephemeral_client
existing_client.upgrade(packet_router, bandwidth_controller, shutdown) {
existing_client.upgrade(packet_router, bandwidth_controller, shutdown)
} else {
let gateway_listener = if wireguard_connection {
if let Some(tun_address) = details.wg_tun_address {
tun_address.to_string()
} else {
let default =
format!("ws://{WG_TUN_DEVICE_ADDRESS}:{DEFAULT_CLIENT_LISTENING_PORT}");
warn!("gateway {} does not have tun device address set. defaulting to '{default}'", details.gateway_id);
default
}
} else { } else {
let cfg = GatewayConfig::new( details.gateway_listener.to_string()
details.gateway_id,
details
.gateway_owner_address
.as_ref()
.map(|o| o.to_string()),
details.gateway_listener.to_string(),
);
GatewayClient::new(
GatewayClientConfig::new_default()
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
.with_response_timeout(
config.debug.gateway_connection.gateway_response_timeout,
),
cfg,
managed_keys.identity_keypair(),
Some(details.derived_aes128_ctr_blake3_hmac_keys),
packet_router,
bandwidth_controller,
shutdown,
)
}; };
let cfg = GatewayConfig::new(
details.gateway_id,
details
.gateway_owner_address
.as_ref()
.map(|o| o.to_string()),
gateway_listener,
);
GatewayClient::new(
cfg,
managed_keys.identity_keypair(),
Some(details.derived_aes128_ctr_blake3_hmac_keys),
packet_router,
bandwidth_controller,
shutdown,
)
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
.with_response_timeout(config.debug.gateway_connection.gateway_response_timeout)
};
gateway_client gateway_client
.authenticate_and_start() .authenticate_and_start()
.await .await
@@ -411,6 +431,7 @@ where
async fn setup_gateway_transceiver( async fn setup_gateway_transceiver(
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>, custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
config: &Config, config: &Config,
wireguard_connection: bool,
initialisation_result: InitialisationResult, initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>, bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter, packet_router: PacketRouter,
@@ -430,7 +451,7 @@ where
Err(ClientCoreError::CustomGatewaySelectionExpected) Err(ClientCoreError::CustomGatewaySelectionExpected)
} else { } else {
// and make sure to invalidate the task client so we wouldn't cause premature shutdown // and make sure to invalidate the task client so we wouldn't cause premature shutdown
shutdown.disarm(); shutdown.mark_as_success();
custom_gateway_transceiver.set_packet_router(packet_router)?; custom_gateway_transceiver.set_packet_router(packet_router)?;
Ok(custom_gateway_transceiver) Ok(custom_gateway_transceiver)
}; };
@@ -439,6 +460,7 @@ where
// otherwise, setup normal gateway client, etc // otherwise, setup normal gateway client, etc
let gateway_client = Self::start_gateway_client( let gateway_client = Self::start_gateway_client(
config, config,
wireguard_connection,
initialisation_result, initialisation_result,
bandwidth_controller, bandwidth_controller,
packet_router, packet_router,
@@ -536,7 +558,7 @@ where
if topology_config.disable_refreshing { if topology_config.disable_refreshing {
// if we're not spawning the refresher, don't cause shutdown immediately // if we're not spawning the refresher, don't cause shutdown immediately
info!("The topology refesher is not going to be started"); info!("The topology refesher is not going to be started");
shutdown.disarm(); shutdown.mark_as_success();
} else { } else {
// don't spawn the refresher if we don't want to be refreshing the topology. // don't spawn the refresher if we don't want to be refreshing the topology.
// only use the initial values obtained // only use the initial values obtained
@@ -703,6 +725,7 @@ where
let gateway_transceiver = Self::setup_gateway_transceiver( let gateway_transceiver = Self::setup_gateway_transceiver(
self.custom_gateway_transceiver, self.custom_gateway_transceiver,
self.config, self.config,
self.wireguard_connection,
init_res, init_res,
bandwidth_controller, bandwidth_controller,
gateway_packet_router, gateway_packet_router,
@@ -102,6 +102,7 @@ pub mod v1_1_33 {
message: format!("the stored gateway listener address was malformed: {err}"), message: format!("the stored gateway listener address was malformed: {err}"),
} }
})?, })?,
wg_tun_address: None,
})) }))
} }
@@ -23,7 +23,7 @@ use crate::{
config::{self, disk_persistence::CommonClientPaths}, config::{self, disk_persistence::CommonClientPaths},
error::ClientCoreError, error::ClientCoreError,
}; };
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-credentials-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage; use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
pub use nym_client_core_gateways_storage as gateways_storage; pub use nym_client_core_gateways_storage as gateways_storage;
@@ -3,12 +3,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use log::{debug, error}; use log::{debug, error};
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter}; pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug; use std::fmt::Debug;
use std::os::raw::c_int as RawFd; use std::os::raw::c_int as RawFd;
use thiserror::Error; use thiserror::Error;
@@ -113,9 +111,8 @@ impl<C, St> RemoteGateway<C, St> {
impl<C, St> GatewayTransceiver for RemoteGateway<C, St> impl<C, St> GatewayTransceiver for RemoteGateway<C, St>
where where
C: DkgQueryClient + Send + Sync, C: Send,
St: CredentialStorage, St: Send,
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{ {
fn gateway_identity(&self) -> identity::PublicKey { fn gateway_identity(&self) -> identity::PublicKey {
self.gateway_client.gateway_identity() self.gateway_client.gateway_identity()
@@ -129,9 +126,8 @@ where
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C, St> GatewaySender for RemoteGateway<C, St> impl<C, St> GatewaySender for RemoteGateway<C, St>
where where
C: DkgQueryClient + Send + Sync, C: Send,
St: CredentialStorage, St: Send,
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{ {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> { async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.gateway_client self.gateway_client
@@ -458,7 +458,7 @@ impl PacketStatisticsControl {
fn report_rates(&self) { fn report_rates(&self) {
if let Some((_, rates)) = self.rates.back() { if let Some((_, rates)) = self.rates.back() {
log::debug!("{}", rates.summary()); log::info!("{}", rates.summary());
log::debug!("{}", rates.detailed_summary()); log::debug!("{}", rates.detailed_summary());
} }
} }
@@ -486,7 +486,7 @@ impl PacketStatisticsControl {
// Check what the number of retransmissions was during the recording window // Check what the number of retransmissions was during the recording window
if let Some((_, start_stats)) = self.history.front() { if let Some((_, start_stats)) = self.history.front() {
let delta = self.stats.clone() - start_stats.clone(); let delta = self.stats.clone() - start_stats.clone();
log::debug!( log::info!(
"mix packet retransmissions/real mix packets: {}/{}", "mix packet retransmissions/real mix packets: {}/{}",
delta.retransmissions_queued, delta.retransmissions_queued,
delta.real_packets_queued, delta.real_packets_queued,
@@ -453,7 +453,6 @@ where
let mut pending_acks = Vec::with_capacity(fragments.len()); let mut pending_acks = Vec::with_capacity(fragments.len());
let mut real_messages = Vec::with_capacity(fragments.len()); let mut real_messages = Vec::with_capacity(fragments.len());
debug!("Splitting message into {} fragments", fragments.len());
for fragment in fragments { for fragment in fragments {
// we need to clone it because we need to keep it in memory in case we had to retransmit // we need to clone it because we need to keep it in memory in case we had to retransmit
// it. And then we'd need to recreate entire ACK again. // it. And then we'd need to recreate entire ACK again.
@@ -474,6 +474,13 @@ where
Poll::Ready(Some((real_messages, conn_id))) => { Poll::Ready(Some((real_messages, conn_id))) => {
log::trace!("handling real_messages: size: {}", real_messages.len()); log::trace!("handling real_messages: size: {}", real_messages.len());
// This is the last step in the pipeline where we know the type of the message, so
// lets count the number of retransmissions here.
if conn_id == TransmissionLane::Retransmission {
self.stats_tx
.report(PacketStatisticsEvent::RetransmissionQueued);
}
// First store what we got for the given connection id // First store what we got for the given connection id
self.transmission_buffer.store(&conn_id, real_messages); self.transmission_buffer.store(&conn_id, real_messages);
let real_next = self.pop_next_message().expect("we just added one"); let real_next = self.pop_next_message().expect("we just added one");
-8
View File
@@ -63,14 +63,6 @@ pub enum ClientCoreError {
source: Box<dyn Error + Send + Sync>, source: Box<dyn Error + Send + Sync>,
}, },
#[error("experienced a failure with our credentials storage: {source}")]
CredentialStoreError {
source: Box<dyn Error + Send + Sync>,
},
#[error("the provided ticket type is invalid")]
UnknownTicketType,
#[error("the gateway id is invalid - {0}")] #[error("the gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError), UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
+18 -40
View File
@@ -46,34 +46,13 @@ const MEASUREMENTS: usize = 3;
const CONN_TIMEOUT: Duration = Duration::from_millis(1500); const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
const PING_TIMEOUT: Duration = Duration::from_millis(1000); const PING_TIMEOUT: Duration = Duration::from_millis(1000);
// The abstraction that some of these helpers use struct GatewayWithLatency<'a> {
pub trait ConnectableGateway { gateway: &'a gateway::Node,
fn identity(&self) -> &identity::PublicKey;
fn clients_address(&self) -> String;
fn is_wss(&self) -> bool;
}
impl ConnectableGateway for gateway::Node {
fn identity(&self) -> &identity::PublicKey {
self.identity()
}
fn clients_address(&self) -> String {
self.clients_address()
}
fn is_wss(&self) -> bool {
self.clients_wss_port.is_some()
}
}
struct GatewayWithLatency<'a, G: ConnectableGateway> {
gateway: &'a G,
latency: Duration, latency: Duration,
} }
impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> { impl<'a> GatewayWithLatency<'a> {
fn new(gateway: &'a G, latency: Duration) -> Self { fn new(gateway: &'a gateway::Node, latency: Duration) -> Self {
GatewayWithLatency { gateway, latency } GatewayWithLatency { gateway, latency }
} }
} }
@@ -151,14 +130,11 @@ async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure) JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
} }
async fn measure_latency<G>(gateway: &G) -> Result<GatewayWithLatency<G>, ClientCoreError> async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
where
G: ConnectableGateway,
{
let addr = gateway.clients_address(); let addr = gateway.clients_address();
trace!( trace!(
"establishing connection to {} ({addr})...", "establishing connection to {} ({addr})...",
gateway.identity(), gateway.identity_key,
); );
let mut stream = connect(&addr).await?; let mut stream = connect(&addr).await?;
@@ -201,7 +177,7 @@ where
let count = results.len() as u64; let count = results.len() as u64;
if count == 0 { if count == 0 {
return Err(ClientCoreError::NoGatewayMeasurements { return Err(ClientCoreError::NoGatewayMeasurements {
identity: gateway.identity().to_base58_string(), identity: gateway.identity_key.to_base58_string(),
}); });
} }
@@ -211,11 +187,11 @@ where
Ok(GatewayWithLatency::new(gateway, avg)) Ok(GatewayWithLatency::new(gateway, avg))
} }
pub async fn choose_gateway_by_latency<'a, R: Rng, G: ConnectableGateway + Clone>( pub async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: &[G], gateways: &[gateway::Node],
must_use_tls: bool, must_use_tls: bool,
) -> Result<G, ClientCoreError> { ) -> Result<gateway::Node, ClientCoreError> {
let gateways = filter_by_tls(gateways, must_use_tls)?; let gateways = filter_by_tls(gateways, must_use_tls)?;
info!( info!(
@@ -247,19 +223,21 @@ pub async fn choose_gateway_by_latency<'a, R: Rng, G: ConnectableGateway + Clone
info!( info!(
"chose gateway {} with average latency of {:?}", "chose gateway {} with average latency of {:?}",
chosen.gateway.identity(), chosen.gateway.identity_key, chosen.latency
chosen.latency
); );
Ok(chosen.gateway.clone()) Ok(chosen.gateway.clone())
} }
fn filter_by_tls<G: ConnectableGateway>( fn filter_by_tls(
gateways: &[G], gateways: &[gateway::Node],
must_use_tls: bool, must_use_tls: bool,
) -> Result<Vec<&G>, ClientCoreError> { ) -> Result<Vec<&gateway::Node>, ClientCoreError> {
if must_use_tls { if must_use_tls {
let filtered = gateways.iter().filter(|g| g.is_wss()).collect::<Vec<_>>(); let filtered = gateways
.iter()
.filter(|g| g.clients_wss_port.is_some())
.collect::<Vec<_>>();
if filtered.is_empty() { if filtered.is_empty() {
return Err(ClientCoreError::NoWssGateways); return Err(ClientCoreError::NoWssGateways);
+20 -6
View File
@@ -23,6 +23,7 @@ use nym_topology::gateway;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use serde::Serialize; use serde::Serialize;
use std::net::IpAddr;
pub mod helpers; pub mod helpers;
pub mod types; pub mod types;
@@ -51,6 +52,7 @@ async fn setup_new_gateway<K, D>(
details_store: &D, details_store: &D,
selection_specification: GatewaySelectionSpecification, selection_specification: GatewaySelectionSpecification,
available_gateways: Vec<gateway::Node>, available_gateways: Vec<gateway::Node>,
wg_tun_ip_address: Option<IpAddr>,
) -> Result<InitialisationResult, ClientCoreError> ) -> Result<InitialisationResult, ClientCoreError>
where where
K: KeyStore, K: KeyStore,
@@ -68,19 +70,19 @@ where
let selected_gateway = match selection_specification { let selected_gateway = match selection_specification {
GatewaySelectionSpecification::UniformRemote { must_use_tls } => { GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?; let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
SelectedGateway::from_topology_node(gateway, must_use_tls)? SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)?
} }
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => { GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
let gateway = let gateway =
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?; choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
SelectedGateway::from_topology_node(gateway, must_use_tls)? SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)?
} }
GatewaySelectionSpecification::Specified { GatewaySelectionSpecification::Specified {
must_use_tls, must_use_tls,
identity, identity,
} => { } => {
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?; let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
SelectedGateway::from_topology_node(gateway, must_use_tls)? SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)?
} }
GatewaySelectionSpecification::Custom { GatewaySelectionSpecification::Custom {
gateway_identity, gateway_identity,
@@ -102,19 +104,23 @@ where
gateway_id, gateway_id,
gateway_owner_address, gateway_owner_address,
gateway_listener, gateway_listener,
wg_tun_address,
} => { } => {
// if we're using a 'normal' gateway setup, do register // if we're using a 'normal' gateway setup, do register
let our_identity = client_keys.identity_keypair(); let our_identity = client_keys.identity_keypair();
// if wg address is set, use that one
let url = wg_tun_address.clone().unwrap_or(gateway_listener.clone());
let registration = let registration =
helpers::register_with_gateway(gateway_id, gateway_listener.clone(), our_identity) helpers::register_with_gateway(gateway_id, url, our_identity).await?;
.await?;
( (
GatewayDetails::new_remote( GatewayDetails::new_remote(
gateway_id, gateway_id,
registration.shared_keys, registration.shared_keys,
gateway_owner_address, gateway_owner_address,
gateway_listener, gateway_listener,
wg_tun_address,
), ),
Some(registration.authenticated_ephemeral_client), Some(registration.authenticated_ephemeral_client),
) )
@@ -201,9 +207,17 @@ where
GatewaySetup::New { GatewaySetup::New {
specification, specification,
available_gateways, available_gateways,
wg_tun_address,
} => { } => {
log::debug!("GatewaySetup::New with spec: {specification:?}"); log::debug!("GatewaySetup::New with spec: {specification:?}");
setup_new_gateway(key_store, details_store, specification, available_gateways).await setup_new_gateway(
key_store,
details_store,
specification,
available_gateways,
wg_tun_address,
)
.await
} }
GatewaySetup::ReuseConnection { GatewaySetup::ReuseConnection {
authenticated_ephemeral_client, authenticated_ephemeral_client,
+41
View File
@@ -6,6 +6,7 @@ use crate::client::key_manager::ClientKeys;
use crate::config::Config; use crate::config::Config;
use crate::error::ClientCoreError; use crate::error::ClientCoreError;
use crate::init::{setup_gateway, use_loaded_gateway_details}; use crate::init::{setup_gateway, use_loaded_gateway_details};
use log::info;
use nym_client_core_gateways_storage::{ use nym_client_core_gateways_storage::{
GatewayRegistration, GatewaysDetailsStore, RemoteGatewayDetails, GatewayRegistration, GatewaysDetailsStore, RemoteGatewayDetails,
}; };
@@ -18,6 +19,7 @@ use nym_validator_client::client::IdentityKey;
use nym_validator_client::nyxd::AccountId; use nym_validator_client::nyxd::AccountId;
use serde::Serialize; use serde::Serialize;
use std::fmt::Display; use std::fmt::Display;
use std::net::IpAddr;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use time::OffsetDateTime; use time::OffsetDateTime;
@@ -30,6 +32,8 @@ pub enum SelectedGateway {
gateway_owner_address: Option<AccountId>, gateway_owner_address: Option<AccountId>,
gateway_listener: Url, gateway_listener: Url,
wg_tun_address: Option<Url>,
}, },
Custom { Custom {
gateway_id: identity::PublicKey, gateway_id: identity::PublicKey,
@@ -37,9 +41,36 @@ pub enum SelectedGateway {
}, },
} }
fn wg_tun_address(
tun_ip: Option<IpAddr>,
gateway: &gateway::Node,
) -> Result<Option<Url>, ClientCoreError> {
let Some(tun_ip) = tun_ip else {
return Ok(None);
};
// log this so we'd remember about it if we ever decided to actually use that port
if gateway.clients_wss_port.is_some() {
info!(
"gateway {} exposes wss but for wireguard we're going to use ws",
gateway.identity_key
);
}
let raw_url = format!("ws://{tun_ip}:{}", gateway.clients_ws_port);
Ok(Some(raw_url.as_str().parse().map_err(|source| {
ClientCoreError::MalformedListener {
gateway_id: gateway.identity_key.to_base58_string(),
raw_listener: raw_url,
source,
}
})?))
}
impl SelectedGateway { impl SelectedGateway {
pub fn from_topology_node( pub fn from_topology_node(
node: gateway::Node, node: gateway::Node,
wg_tun_ip_address: Option<IpAddr>,
must_use_tls: bool, must_use_tls: bool,
) -> Result<Self, ClientCoreError> { ) -> Result<Self, ClientCoreError> {
let gateway_listener = if must_use_tls { let gateway_listener = if must_use_tls {
@@ -51,6 +82,8 @@ impl SelectedGateway {
node.clients_address() node.clients_address()
}; };
let wg_tun_address = wg_tun_address(wg_tun_ip_address, &node)?;
let gateway_owner_address = node let gateway_owner_address = node
.owner .owner
.as_ref() .as_ref()
@@ -76,6 +109,7 @@ impl SelectedGateway {
gateway_id: node.identity_key, gateway_id: node.identity_key,
gateway_owner_address, gateway_owner_address,
gateway_listener, gateway_listener,
wg_tun_address,
}) })
} }
@@ -216,6 +250,12 @@ pub enum GatewaySetup {
// TODO: seems to be a bit inefficient to pass them by value // TODO: seems to be a bit inefficient to pass them by value
available_gateways: Vec<gateway::Node>, available_gateways: Vec<gateway::Node>,
/// Implicitly specify whether the chosen gateway must use wireguard mode by setting the tun address.
///
/// Currently this is imperfect solution as I'd imagine this address could vary from gateway to gateway
/// so perhaps it should be part of gateway::Node struct
wg_tun_address: Option<IpAddr>,
}, },
ReuseConnection { ReuseConnection {
@@ -250,6 +290,7 @@ impl GatewaySetup {
additional_data: None, additional_data: None,
}, },
available_gateways: vec![], available_gateways: vec![],
wg_tun_address: None,
} }
} }
-2
View File
@@ -2,9 +2,7 @@ use std::future::Future;
#[cfg(all( #[cfg(all(
not(target_arch = "wasm32"), not(target_arch = "wasm32"),
feature = "cli",
feature = "fs-surb-storage", feature = "fs-surb-storage",
feature = "fs-credentials-storage",
feature = "fs-gateways-storage" feature = "fs-gateways-storage"
))] ))]
pub mod cli_helpers; pub mod cli_helpers;
+2 -2
View File
@@ -24,7 +24,7 @@ nym-bandwidth-controller = { path = "../../bandwidth-controller" }
nym-credentials = { path = "../../credentials" } nym-credentials = { path = "../../credentials" }
nym-credential-storage = { path = "../../credential-storage" } nym-credential-storage = { path = "../../credential-storage" }
nym-crypto = { path = "../../crypto" } nym-crypto = { path = "../../crypto" }
nym-gateway-requests = { path = "../../gateway-requests" } nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../network-defaults" } nym-network-defaults = { path = "../../network-defaults" }
nym-sphinx = { path = "../../nymsphinx" } nym-sphinx = { path = "../../nymsphinx" }
nym-pemstore = { path = "../../pemstore" } nym-pemstore = { path = "../../pemstore" }
@@ -43,7 +43,7 @@ workspace = true
features = ["macros", "rt", "net", "sync", "time"] features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.16" version = "0.1.11"
features = ["net", "sync", "time"] features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
@@ -1,89 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use si_scale::helpers::bibytes2;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
#[derive(Clone, Default)]
pub struct ClientBandwidth {
inner: Arc<ClientBandwidthInner>,
}
#[derive(Default)]
struct ClientBandwidthInner {
/// the actual bandwidth amount (in bytes) available
available: AtomicI64,
/// defines the timestamp when the bandwidth information has been logged to the logs stream
last_logged_ts: AtomicI64,
/// defines the timestamp when the bandwidth value was last updated
last_updated_ts: AtomicI64,
}
impl ClientBandwidth {
pub(crate) fn new_empty() -> Self {
ClientBandwidth {
inner: Arc::new(ClientBandwidthInner {
available: AtomicI64::new(0),
last_logged_ts: AtomicI64::new(0),
last_updated_ts: AtomicI64::new(0),
}),
}
}
pub(crate) fn remaining(&self) -> i64 {
self.inner.available.load(Ordering::Acquire)
}
pub(crate) fn maybe_log_bandwidth(&self, now: Option<OffsetDateTime>) {
let last = self.last_logged();
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
if last + Duration::from_secs(10) < now {
self.log_bandwidth(Some(now))
}
}
pub(crate) fn log_bandwidth(&self, now: Option<OffsetDateTime>) {
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
let remaining = self.remaining();
let remaining_bi2 = bibytes2(remaining as f64);
if remaining < 0 {
log::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
} else {
log::info!("remaining bandwidth: {remaining_bi2}");
}
self.inner
.last_logged_ts
.store(now.unix_timestamp(), Ordering::Relaxed)
}
pub(crate) fn update_and_maybe_log(&self, remaining: i64) {
let now = OffsetDateTime::now_utc();
self.inner.available.store(remaining, Ordering::Release);
self.inner
.last_updated_ts
.store(now.unix_timestamp(), Ordering::Relaxed);
self.maybe_log_bandwidth(Some(now))
}
pub(crate) fn update_and_log(&self, remaining: i64) {
let now = OffsetDateTime::now_utc();
self.inner.available.store(remaining, Ordering::Release);
self.inner
.last_updated_ts
.store(now.unix_timestamp(), Ordering::Relaxed);
self.log_bandwidth(Some(now))
}
fn last_logged(&self) -> OffsetDateTime {
// SAFETY: this value is always populated with valid timestamps
OffsetDateTime::from_unix_timestamp(self.inner.last_logged_ts.load(Ordering::Relaxed))
.unwrap()
}
}
@@ -1,13 +1,12 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::bandwidth::ClientBandwidth;
use crate::client::config::GatewayClientConfig;
use crate::error::GatewayClientError; use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter; use crate::packet_router::PacketRouter;
pub use crate::packet_router::{ pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender, AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
}; };
use crate::socket_state::{ws_fd, PartiallyDelegatedHandle, SocketState}; use crate::socket_state::{ws_fd, PartiallyDelegated, SocketState};
use crate::traits::GatewayPacketRouter; use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_message, try_decrypt_binary_message}; use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
@@ -24,11 +23,14 @@ use nym_gateway_requests::{
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
CURRENT_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION,
}; };
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_task::TaskClient; use nym_task::TaskClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use tungstenite::protocol::Message; use tungstenite::protocol::Message;
use url::Url; use url::Url;
@@ -46,7 +48,12 @@ use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep; use wasmtimer::tokio::sleep;
pub mod config; // 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 struct GatewayConfig {
pub gateway_identity: identity::PublicKey, pub gateway_identity: identity::PublicKey,
@@ -72,19 +79,29 @@ impl GatewayConfig {
} }
// TODO: this should be refactored into a state machine that keeps track of its authentication state // TODO: this should be refactored into a state machine that keeps track of its authentication state
#[derive(Debug)]
pub struct GatewayClient<C, St = EphemeralCredentialStorage> { pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
pub cfg: GatewayClientConfig,
authenticated: bool, authenticated: bool,
bandwidth: ClientBandwidth, disabled_credentials_mode: bool,
bandwidth_remaining: i64,
gateway_address: String, gateway_address: String,
gateway_identity: identity::PublicKey, gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>, local_identity: Arc<identity::KeyPair>,
shared_key: Option<Arc<SharedKeys>>, shared_key: Option<Arc<SharedKeys>>,
connection: SocketState, connection: SocketState,
packet_router: PacketRouter, packet_router: PacketRouter,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, St>>, bandwidth_controller: Option<BandwidthController<C, St>>,
// reconnection related variables
/// Specifies whether client should try to reconnect to gateway on connection failure.
should_reconnect_on_failure: bool,
/// Specifies maximum number of attempts client will try to reconnect to gateway on failure
/// before giving up.
reconnection_attempts: usize,
/// Delay between each subsequent reconnection attempt.
reconnection_backoff: Duration,
// currently unused (but populated) // currently unused (but populated)
negotiated_protocol: Option<u8>, negotiated_protocol: Option<u8>,
@@ -94,8 +111,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
impl<C, St> GatewayClient<C, St> { impl<C, St> GatewayClient<C, St> {
pub fn new( pub fn new(
cfg: GatewayClientConfig, config: GatewayConfig,
gateway_config: GatewayConfig,
local_identity: Arc<identity::KeyPair>, local_identity: Arc<identity::KeyPair>,
// TODO: make it mandatory. if you don't want to pass it, use `new_init` // TODO: make it mandatory. if you don't want to pass it, use `new_init`
shared_key: Option<Arc<SharedKeys>>, shared_key: Option<Arc<SharedKeys>>,
@@ -104,21 +120,55 @@ impl<C, St> GatewayClient<C, St> {
task_client: TaskClient, task_client: TaskClient,
) -> Self { ) -> Self {
GatewayClient { GatewayClient {
cfg,
authenticated: false, authenticated: false,
bandwidth: ClientBandwidth::new_empty(), disabled_credentials_mode: true,
gateway_address: gateway_config.gateway_listener, bandwidth_remaining: 0,
gateway_identity: gateway_config.gateway_identity, gateway_address: config.gateway_listener,
gateway_identity: config.gateway_identity,
local_identity, local_identity,
shared_key, shared_key,
connection: SocketState::NotConnected, connection: SocketState::NotConnected,
packet_router, packet_router,
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
bandwidth_controller, bandwidth_controller,
should_reconnect_on_failure: true,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
negotiated_protocol: None, negotiated_protocol: None,
task_client, task_client,
} }
} }
#[must_use]
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
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
}
#[must_use]
pub fn with_response_timeout(mut self, response_timeout_duration: Duration) -> Self {
self.response_timeout_duration = response_timeout_duration;
self
}
#[must_use]
pub fn with_reconnection_attempts(mut self, reconnection_attempts: usize) -> Self {
self.reconnection_attempts = reconnection_attempts;
self
}
#[must_use]
pub fn with_reconnection_backoff(mut self, backoff: Duration) -> Self {
self.reconnection_backoff = backoff;
self
}
pub fn gateway_identity(&self) -> identity::PublicKey { pub fn gateway_identity(&self) -> identity::PublicKey {
self.gateway_identity self.gateway_identity
} }
@@ -132,7 +182,7 @@ impl<C, St> GatewayClient<C, St> {
} }
pub fn remaining_bandwidth(&self) -> i64 { pub fn remaining_bandwidth(&self) -> i64 {
self.bandwidth.remaining() self.bandwidth_remaining
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -208,21 +258,18 @@ impl<C, St> GatewayClient<C, St> {
info!("Attempting gateway reconnection..."); info!("Attempting gateway reconnection...");
self.authenticated = false; self.authenticated = false;
for i in 1..self.cfg.connection.reconnection_attempts { for i in 1..self.reconnection_attempts {
info!("reconnection attempt {}...", i); info!("attempt {}...", i);
if self.try_reconnect().await.is_ok() { if self.try_reconnect().await.is_ok() {
info!("managed to reconnect!"); info!("managed to reconnect!");
return Ok(()); return Ok(());
} }
sleep(self.cfg.connection.reconnection_backoff).await; sleep(self.reconnection_backoff).await;
} }
// final attempt (done separately to be able to return a proper error) // final attempt (done separately to be able to return a proper error)
info!( info!("attempt {}", self.reconnection_attempts);
"reconnection attempt {}",
self.cfg.connection.reconnection_attempts
);
match self.try_reconnect().await { match self.try_reconnect().await {
Ok(_) => { Ok(_) => {
info!("managed to reconnect!"); info!("managed to reconnect!");
@@ -231,7 +278,7 @@ impl<C, St> GatewayClient<C, St> {
Err(err) => { Err(err) => {
error!( error!(
"failed to reconnect after {} attempts", "failed to reconnect after {} attempts",
self.cfg.connection.reconnection_attempts self.reconnection_attempts
); );
Err(err) Err(err)
} }
@@ -247,7 +294,7 @@ impl<C, St> GatewayClient<C, St> {
_ => return Err(GatewayClientError::ConnectionInInvalidState), _ => return Err(GatewayClientError::ConnectionInInvalidState),
}; };
let timeout = sleep(self.cfg.connection.response_timeout_duration); let timeout = sleep(self.response_timeout_duration);
tokio::pin!(timeout); tokio::pin!(timeout);
loop { loop {
@@ -416,9 +463,7 @@ impl<C, St> GatewayClient<C, St> {
ws_stream, ws_stream,
self.local_identity.as_ref(), self.local_identity.as_ref(),
self.gateway_identity, self.gateway_identity,
self.cfg.bandwidth.require_tickets, !self.disabled_credentials_mode,
#[cfg(not(target_arch = "wasm32"))]
self.task_client.clone(),
) )
.await .await
.map_err(GatewayClientError::RegistrationFailure), .map_err(GatewayClientError::RegistrationFailure),
@@ -480,7 +525,7 @@ impl<C, St> GatewayClient<C, St> {
self_address, self_address,
encrypted_address, encrypted_address,
iv, iv,
self.cfg.bandwidth.require_tickets, !self.disabled_credentials_mode,
) )
.into(); .into();
@@ -492,8 +537,7 @@ impl<C, St> GatewayClient<C, St> {
} => { } => {
self.check_gateway_protocol(protocol_version)?; self.check_gateway_protocol(protocol_version)?;
self.authenticated = status; self.authenticated = status;
self.bandwidth.update_and_maybe_log(bandwidth_remaining); self.bandwidth_remaining = bandwidth_remaining;
self.negotiated_protocol = protocol_version; self.negotiated_protocol = protocol_version;
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}"); log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
self.task_client.send_status_msg(Box::new( self.task_client.send_status_msg(Box::new(
@@ -532,74 +576,56 @@ impl<C, St> GatewayClient<C, St> {
} }
} }
async fn claim_ecash_bandwidth( async fn claim_coconut_bandwidth(
&mut self, &mut self,
credential: CredentialSpendingData, credential: CredentialSpendingData,
) -> Result<(), GatewayClientError> { ) -> Result<(), GatewayClientError> {
let mut rng = OsRng; let mut rng = OsRng;
let iv = IV::new_random(&mut rng); let iv = IV::new_random(&mut rng);
let msg = ClientControlRequest::new_enc_ecash_credential( let msg = ClientControlRequest::new_enc_coconut_bandwidth_credential_v2(
credential, credential,
self.shared_key.as_ref().unwrap(), self.shared_key.as_ref().unwrap(),
iv, iv,
) )
.into(); .into();
let bandwidth_remaining = match self.send_websocket_message(msg).await? { self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { available_total } => Ok(available_total), ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
ServerResponse::TypedError { error } => {
Err(GatewayClientError::TypedGatewayError(error))
}
_ => Err(GatewayClientError::UnexpectedResponse), _ => Err(GatewayClientError::UnexpectedResponse),
}?; }?;
// TODO: create tracing span
info!("managed to claim ecash bandwidth");
self.bandwidth.update_and_log(bandwidth_remaining);
Ok(()) Ok(())
} }
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> { async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth.into(); let msg = ClientControlRequest::ClaimFreeTestnetBandwidth.into();
let bandwidth_remaining = match self.send_websocket_message(msg).await? { self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { available_total } => Ok(available_total), ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse), _ => Err(GatewayClientError::UnexpectedResponse),
}?; }?;
info!("managed to claim testnet bandwidth");
self.bandwidth.update_and_log(bandwidth_remaining);
Ok(()) Ok(())
} }
fn unchecked_bandwidth_controller(&self) -> &BandwidthController<C, St> {
self.bandwidth_controller.as_ref().unwrap()
}
pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError> pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError>
where where
C: DkgQueryClient + Send + Sync, C: DkgQueryClient + Send + Sync,
St: CredentialStorage, St: CredentialStorage,
<St as CredentialStorage>::StorageError: Send + Sync + 'static, <St as CredentialStorage>::StorageError: Send + Sync + 'static,
{ {
// TODO: make it configurable
const TICKETS_TO_SPEND: u32 = 1;
if !self.authenticated { if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated); return Err(GatewayClientError::NotAuthenticated);
} }
if self.shared_key.is_none() { if self.shared_key.is_none() {
return Err(GatewayClientError::NoSharedKeyAvailable); return Err(GatewayClientError::NoSharedKeyAvailable);
} }
if self.bandwidth_controller.is_none() && self.cfg.bandwidth.require_tickets { if self.bandwidth_controller.is_none() && !self.disabled_credentials_mode {
return Err(GatewayClientError::NoBandwidthControllerAvailable); return Err(GatewayClientError::NoBandwidthControllerAvailable);
} }
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while"); warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
if !self.cfg.bandwidth.require_tickets { if self.disabled_credentials_mode {
info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential"); info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential");
return self.try_claim_testnet_bandwidth().await; return self.try_claim_testnet_bandwidth().await;
} }
@@ -615,52 +641,49 @@ impl<C, St> GatewayClient<C, St> {
negotiated_protocol: Some(gateway_protocol), negotiated_protocol: Some(gateway_protocol),
}); });
} }
let gateway_id = self.gateway_identity().to_base58_string();
let prepared_credential = self let prepared_credential = self
.unchecked_bandwidth_controller() .bandwidth_controller
.prepare_ecash_ticket(self.gateway_identity.to_bytes(), TICKETS_TO_SPEND) .as_ref()
.unwrap()
.prepare_bandwidth_credential(&gateway_id)
.await?; .await?;
match self.claim_ecash_bandwidth(prepared_credential.data).await { self.claim_coconut_bandwidth(prepared_credential.data)
Ok(_) => Ok(()), .await?;
Err(err) => { self.bandwidth_controller
error!("failed to claim ecash bandwidth with the gateway...: {err}"); .as_ref()
if err.is_ticket_replay() { .unwrap()
warn!("this was due to our ticket being replayed! have you messed with the database file?") .consume_credential(prepared_credential.credential_id, &gateway_id)
} else { .await?;
// TODO: tracing span
info!("attempting to revert ticket withdrawal...");
self.unchecked_bandwidth_controller()
.attempt_revert_ticket_usage(prepared_credential.metadata)
.await?;
}
Err(err) Ok(())
} }
}
fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 {
packets
.iter()
.map(|packet| packet.packet().len())
.sum::<usize>() as i64
} }
pub async fn batch_send_mix_packets( pub async fn batch_send_mix_packets(
&mut self, &mut self,
packets: Vec<MixPacket>, packets: Vec<MixPacket>,
) -> Result<(), GatewayClientError> ) -> Result<(), GatewayClientError> {
where
C: DkgQueryClient + Send + Sync,
St: CredentialStorage,
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{
debug!("Sending {} mix packets", packets.len()); debug!("Sending {} mix packets", packets.len());
if !self.authenticated { if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated); return Err(GatewayClientError::NotAuthenticated);
} }
let bandwidth_remaining = self.bandwidth.remaining(); if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
if bandwidth_remaining < self.cfg.bandwidth.remaining_bandwidth_threshold { return Err(GatewayClientError::NotEnoughBandwidth(
self.cfg self.estimate_required_bandwidth(&packets),
.bandwidth self.bandwidth_remaining,
.ensure_above_cutoff(bandwidth_remaining)?; ));
self.claim_bandwidth().await?;
} }
if !self.connection.is_established() { if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished); return Err(GatewayClientError::ConnectionNotEstablished);
} }
@@ -680,7 +703,7 @@ impl<C, St> GatewayClient<C, St> {
.batch_send_websocket_messages_without_response(messages) .batch_send_websocket_messages_without_response(messages)
.await .await
{ {
if err.is_closed_connection() && self.cfg.connection.should_reconnect_on_failure { if err.is_closed_connection() && self.should_reconnect_on_failure {
self.attempt_reconnection().await self.attempt_reconnection().await
} else { } else {
Err(err) Err(err)
@@ -695,7 +718,7 @@ impl<C, St> GatewayClient<C, St> {
msg: Message, msg: Message,
) -> Result<(), GatewayClientError> { ) -> Result<(), GatewayClientError> {
if let Err(err) = self.send_websocket_message_without_response(msg).await { if let Err(err) = self.send_websocket_message_without_response(msg).await {
if err.is_closed_connection() && self.cfg.connection.should_reconnect_on_failure { if err.is_closed_connection() && self.should_reconnect_on_failure {
debug!("Going to attempt a reconnection"); debug!("Going to attempt a reconnection");
self.attempt_reconnection().await self.attempt_reconnection().await
} else { } else {
@@ -719,23 +742,19 @@ impl<C, St> GatewayClient<C, St> {
} }
// TODO: possibly make responses optional // TODO: possibly make responses optional
pub async fn send_mix_packet(&mut self, mix_packet: MixPacket) -> Result<(), GatewayClientError> pub async fn send_mix_packet(
where &mut self,
C: DkgQueryClient + Send + Sync, mix_packet: MixPacket,
St: CredentialStorage, ) -> Result<(), GatewayClientError> {
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{
if !self.authenticated { if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated); return Err(GatewayClientError::NotAuthenticated);
} }
let bandwidth_remaining = self.bandwidth.remaining(); if (mix_packet.packet().len() as i64) > self.bandwidth_remaining {
if bandwidth_remaining < self.cfg.bandwidth.remaining_bandwidth_threshold { return Err(GatewayClientError::NotEnoughBandwidth(
self.cfg mix_packet.packet().len() as i64,
.bandwidth self.bandwidth_remaining,
.ensure_above_cutoff(bandwidth_remaining)?; ));
self.claim_bandwidth().await?;
} }
if !self.connection.is_established() { if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished); return Err(GatewayClientError::ConnectionNotEstablished);
} }
@@ -781,7 +800,7 @@ impl<C, St> GatewayClient<C, St> {
let partially_delegated = let partially_delegated =
match std::mem::replace(&mut self.connection, SocketState::Invalid) { match std::mem::replace(&mut self.connection, SocketState::Invalid) {
SocketState::Available(conn) => { SocketState::Available(conn) => {
PartiallyDelegatedHandle::split_and_listen_for_mixnet_messages( PartiallyDelegated::split_and_listen_for_mixnet_messages(
*conn, *conn,
self.packet_router.clone(), self.packet_router.clone(),
Arc::clone( Arc::clone(
@@ -789,7 +808,6 @@ impl<C, St> GatewayClient<C, St> {
.as_ref() .as_ref()
.expect("no shared key present even though we're authenticated!"), .expect("no shared key present even though we're authenticated!"),
), ),
self.bandwidth.clone(),
self.task_client.clone(), self.task_client.clone(),
) )
} }
@@ -830,12 +848,10 @@ impl<C, St> GatewayClient<C, St> {
self.establish_connection().await?; self.establish_connection().await?;
} }
let shared_key = self.perform_initial_authentication().await?; let shared_key = self.perform_initial_authentication().await?;
let bandwidth_remaining = self.bandwidth.remaining();
if bandwidth_remaining < self.cfg.bandwidth.remaining_bandwidth_threshold { if self.bandwidth_remaining < REMAINING_BANDWIDTH_THRESHOLD {
self.cfg info!("Claiming more bandwidth for your tokens. This will use {} token(s) from your wallet. \
.bandwidth Stop the process now if you don't want that to happen.", TOKENS_TO_BURN);
.ensure_above_cutoff(bandwidth_remaining)?;
info!("Claiming more bandwidth with existing credentials. Stop the process now if you don't want that to happen.");
self.claim_bandwidth().await?; self.claim_bandwidth().await?;
} }
@@ -870,16 +886,20 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
let packet_router = PacketRouter::new(ack_tx, mix_tx, task_client.clone()); let packet_router = PacketRouter::new(ack_tx, mix_tx, task_client.clone());
GatewayClient { GatewayClient {
cfg: GatewayClientConfig::default().with_disabled_credentials_mode(true),
authenticated: false, authenticated: false,
bandwidth: ClientBandwidth::new_empty(), disabled_credentials_mode: true,
bandwidth_remaining: 0,
gateway_address: gateway_listener.to_string(), gateway_address: gateway_listener.to_string(),
gateway_identity, gateway_identity,
local_identity, local_identity,
shared_key: None, shared_key: None,
connection: SocketState::NotConnected, connection: SocketState::NotConnected,
packet_router, packet_router,
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
bandwidth_controller: None, bandwidth_controller: None,
should_reconnect_on_failure: false,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
negotiated_protocol: None, negotiated_protocol: None,
task_client, task_client,
} }
@@ -898,16 +918,20 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
assert!(self.shared_key.is_some()); assert!(self.shared_key.is_some());
GatewayClient { GatewayClient {
cfg: self.cfg,
authenticated: self.authenticated, authenticated: self.authenticated,
bandwidth: self.bandwidth, disabled_credentials_mode: self.disabled_credentials_mode,
bandwidth_remaining: self.bandwidth_remaining,
gateway_address: self.gateway_address, gateway_address: self.gateway_address,
gateway_identity: self.gateway_identity, gateway_identity: self.gateway_identity,
local_identity: self.local_identity, local_identity: self.local_identity,
shared_key: self.shared_key, shared_key: self.shared_key,
connection: self.connection, connection: self.connection,
packet_router, packet_router,
response_timeout_duration: self.response_timeout_duration,
bandwidth_controller, bandwidth_controller,
should_reconnect_on_failure: self.should_reconnect_on_failure,
reconnection_attempts: self.reconnection_attempts,
reconnection_backoff: self.reconnection_backoff,
negotiated_protocol: self.negotiated_protocol, negotiated_protocol: self.negotiated_protocol,
task_client, task_client,
} }
@@ -1,135 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::GatewayClientError;
use nym_network_defaults::TicketTypeRepr::V1MixnetEntry;
use si_scale::helpers::bibytes2;
use std::time::Duration;
#[derive(Debug, Default, Clone, Copy)]
pub struct GatewayClientConfig {
pub connection: Connection,
pub bandwidth: BandwidthTickets,
}
impl GatewayClientConfig {
pub fn new_default() -> Self {
Default::default()
}
#[must_use]
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
self.bandwidth.require_tickets = !disabled_credentials_mode;
self
}
#[must_use]
pub fn with_reconnection_on_failure(mut self, should_reconnect_on_failure: bool) -> Self {
self.connection.should_reconnect_on_failure = should_reconnect_on_failure;
self
}
#[must_use]
pub fn with_response_timeout(mut self, response_timeout_duration: Duration) -> Self {
self.connection.response_timeout_duration = response_timeout_duration;
self
}
#[must_use]
pub fn with_reconnection_attempts(mut self, reconnection_attempts: usize) -> Self {
self.connection.reconnection_attempts = reconnection_attempts;
self
}
#[must_use]
pub fn with_reconnection_backoff(mut self, backoff: Duration) -> Self {
self.connection.reconnection_backoff = backoff;
self
}
}
#[derive(Debug, Clone, Copy)]
pub struct Connection {
/// Specifies the timeout for gateway responses
pub response_timeout_duration: Duration,
/// Specifies whether client should try to reconnect to gateway on connection failure.
pub should_reconnect_on_failure: bool,
/// Specifies maximum number of attempts client will try to reconnect to gateway on failure
/// before giving up.
pub reconnection_attempts: usize,
/// Delay between each subsequent reconnection attempt.
pub reconnection_backoff: Duration,
}
impl Connection {
// 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
pub const DEFAULT_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
pub const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
pub const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
}
impl Default for Connection {
fn default() -> Self {
Connection {
response_timeout_duration: Self::DEFAULT_RESPONSE_TIMEOUT,
should_reconnect_on_failure: true,
reconnection_attempts: Self::DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: Self::DEFAULT_RECONNECTION_BACKOFF,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BandwidthTickets {
/// specifies whether this client will be sending bandwidth tickets or will attempt to use 'free' testnet bandwidth instead
pub require_tickets: bool,
/// specifies threshold (in bytes) under which the client should send another ticket to the gateway
pub remaining_bandwidth_threshold: i64,
/// specifies threshold (in bytes) under which the client will NOT send any tickets because it got accused of double spending and got its bandwidth revoked
/// if not specified, the client will always send tickets
pub cutoff_remaining_bandwidth_threshold: Option<i64>,
}
impl BandwidthTickets {
// TO BE CHANGED \/
pub const DEFAULT_REQUIRES_TICKETS: bool = false;
// 20% of entry ticket value
pub const DEFAULT_REMAINING_BANDWIDTH_THRESHOLD: i64 =
(V1MixnetEntry.bandwidth_value() / 5) as i64;
pub const DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD: Option<i64> = None;
pub fn ensure_above_cutoff(&self, available: i64) -> Result<(), GatewayClientError> {
if let Some(cutoff) = self.cutoff_remaining_bandwidth_threshold {
if available < cutoff {
let available_bi2 = bibytes2(available as f64);
let cutoff_bi2 = bibytes2(cutoff as f64);
return Err(GatewayClientError::BandwidthBelowCutoffValue {
available_bi2,
cutoff_bi2,
});
}
}
Ok(())
}
}
impl Default for BandwidthTickets {
fn default() -> Self {
BandwidthTickets {
require_tickets: Self::DEFAULT_REQUIRES_TICKETS,
remaining_bandwidth_threshold: Self::DEFAULT_REMAINING_BANDWIDTH_THRESHOLD,
cutoff_remaining_bandwidth_threshold:
Self::DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD,
}
}
}
+3 -21
View File
@@ -1,26 +1,21 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#[cfg(target_arch = "wasm32")]
use gloo_utils::errors::JsError;
use nym_gateway_requests::registration::handshake::error::HandshakeError; use nym_gateway_requests::registration::handshake::error::HandshakeError;
use nym_gateway_requests::SimpleGatewayRequestsError;
use std::io; use std::io;
use thiserror::Error; use thiserror::Error;
use tungstenite::Error as WsError; use tungstenite::Error as WsError;
#[cfg(target_arch = "wasm32")]
use gloo_utils::errors::JsError;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum GatewayClientError { pub enum GatewayClientError {
#[error("Connection to the gateway is not established")] #[error("Connection to the gateway is not established")]
ConnectionNotEstablished, ConnectionNotEstablished,
#[error("gateway returned an error response: {0}")] #[error("Gateway returned an error response: {0}")]
GatewayError(String), GatewayError(String),
#[error("gateway returned an error response: {0}")]
TypedGatewayError(SimpleGatewayRequestsError),
#[error("There was a network error: {0}")] #[error("There was a network error: {0}")]
NetworkError(#[from] WsError), NetworkError(#[from] WsError),
@@ -67,12 +62,6 @@ pub enum GatewayClientError {
#[error("There are no more bandwidth credentials acquired. Please buy some more if you want to use the mixnet")] #[error("There are no more bandwidth credentials acquired. Please buy some more if you want to use the mixnet")]
NoMoreBandwidthCredentials, NoMoreBandwidthCredentials,
#[error("the current available bandwidth ({available_bi2}) is below the minimum cutoff threshold off {cutoff_bi2}")]
BandwidthBelowCutoffValue {
available_bi2: String,
cutoff_bi2: String,
},
#[error("Received an unexpected response")] #[error("Received an unexpected response")]
UnexpectedResponse, UnexpectedResponse,
@@ -124,11 +113,4 @@ impl GatewayClientError {
_ => false, _ => false,
} }
} }
pub fn is_ticket_replay(&self) -> bool {
match self {
GatewayClientError::TypedGatewayError(err) => err.is_ticket_replay(),
_ => false,
}
}
} }
+5 -3
View File
@@ -6,7 +6,7 @@ use log::warn;
use nym_gateway_requests::BinaryResponse; use nym_gateway_requests::BinaryResponse;
use tungstenite::{protocol::Message, Error as WsError}; use tungstenite::{protocol::Message, Error as WsError};
pub use client::{config::GatewayClientConfig, GatewayClient, GatewayConfig}; pub use client::{GatewayClient, GatewayConfig};
pub use nym_gateway_requests::registration::handshake::SharedKeys; pub use nym_gateway_requests::registration::handshake::SharedKeys;
pub use packet_router::{ pub use packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender, AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
@@ -14,7 +14,6 @@ pub use packet_router::{
}; };
pub use traits::GatewayPacketRouter; pub use traits::GatewayPacketRouter;
mod bandwidth;
pub mod client; pub mod client;
pub mod error; pub mod error;
pub mod packet_router; pub mod packet_router;
@@ -52,7 +51,10 @@ pub(crate) fn try_decrypt_binary_message(
BinaryResponse::PushedMixMessage(plaintext) => Some(plaintext), BinaryResponse::PushedMixMessage(plaintext) => Some(plaintext),
}, },
Err(err) => { Err(err) => {
warn!("message received from the gateway was malformed! - {err}",); warn!(
"message received from the gateway was malformed! - {:?}",
err
);
None None
} }
} }
@@ -70,8 +70,8 @@ impl PacketRouter {
Ok(()) Ok(())
} }
pub fn disarm(&mut self) { pub fn mark_as_success(&mut self) {
self.shutdown.disarm(); self.shutdown.mark_as_success();
} }
} }
@@ -1,7 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::bandwidth::ClientBandwidth;
use crate::error::GatewayClientError; use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter; use crate::packet_router::PacketRouter;
use crate::traits::GatewayPacketRouter; use crate::traits::GatewayPacketRouter;
@@ -11,13 +10,16 @@ use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use log::*; use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys; use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::{ServerResponse, SimpleGatewayRequestsError}; use nym_gateway_requests::ServerResponse;
use nym_task::TaskClient; use nym_task::TaskClient;
use std::os::raw::c_int as RawFd;
use std::sync::Arc;
use tungstenite::{protocol::Message, Error as WsError};
use si_scale::helpers::bibytes2; use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
use tungstenite::Message;
#[cfg(unix)] #[cfg(unix)]
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -40,7 +42,6 @@ type WsConn = JSWebsocket;
// by some other task, however, we can notify it to get the stream back. // by some other task, however, we can notify it to get the stream back.
type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>; type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>;
type SplitStreamSender = oneshot::Sender<Result<SplitStream<WsConn>, GatewayClientError>>;
pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> { pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)] #[cfg(unix)]
@@ -52,204 +53,92 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
None None
} }
// disgusting? absolutely, but does the trick for now
static LAST_LOGGED_BANDWIDTH_TS: AtomicI64 = AtomicI64::new(0);
fn maybe_log_bandwidth(remaining: i64) {
// SAFETY: this value is always populated with valid timestamps
let last =
OffsetDateTime::from_unix_timestamp(LAST_LOGGED_BANDWIDTH_TS.load(Ordering::Relaxed))
.unwrap();
let now = OffsetDateTime::now_utc();
if last + Duration::from_secs(10) < now {
log::info!("remaining bandwidth: {}", bibytes2(remaining as f64));
LAST_LOGGED_BANDWIDTH_TS.store(now.unix_timestamp(), Ordering::Relaxed)
}
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct PartiallyDelegatedHandle { pub(crate) struct PartiallyDelegated {
sink_half: SplitSink<WsConn, Message>, sink_half: SplitSink<WsConn, Message>,
// this could have been simplified by a notify as opposed to oneshot, but let's not change what ain't broke
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>), delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
ws_fd: Option<RawFd>, ws_fd: Option<RawFd>,
} }
struct PartiallyDelegatedRouter { impl PartiallyDelegated {
packet_router: PacketRouter, fn recover_received_plaintexts(
shared_key: Arc<SharedKeys>, ws_msgs: Vec<Message>,
client_bandwidth: ClientBandwidth, shared_key: &SharedKeys,
) -> Result<Vec<Vec<u8>>, GatewayClientError> {
stream_return: SplitStreamSender, let mut plaintexts = Vec::with_capacity(ws_msgs.len());
stream_return_requester: oneshot::Receiver<()>, for ws_msg in ws_msgs {
} match ws_msg {
Message::Binary(bin_msg) => {
impl PartiallyDelegatedRouter { // this function decrypts the request and checks the MAC
fn new( if let Some(plaintext) = try_decrypt_binary_message(bin_msg, shared_key) {
packet_router: PacketRouter, plaintexts.push(plaintext)
shared_key: Arc<SharedKeys>,
client_bandwidth: ClientBandwidth,
stream_return: SplitStreamSender,
stream_return_requester: oneshot::Receiver<()>,
) -> PartiallyDelegatedRouter {
PartiallyDelegatedRouter {
packet_router,
shared_key,
client_bandwidth,
stream_return,
stream_return_requester,
}
}
async fn run(mut self, mut split_stream: SplitStream<WsConn>, mut task_client: TaskClient) {
let mut chunked_stream = (&mut split_stream).ready_chunks(8);
let ret: Result<_, GatewayClientError> = loop {
tokio::select! {
biased;
// received system-wide shutdown
_ = task_client.recv() => {
log::trace!("GatewayClient listener: Received shutdown");
log::debug!("GatewayClient listener: Exiting");
return;
}
// received request to stop the task and return the stream
_ = &mut self.stream_return_requester => {
log::debug!("received request to return the split ws stream");
break Ok(())
}
socket_msgs = chunked_stream.next() => {
if let Err(err) = self.handle_socket_messages(socket_msgs) {
break Err(err)
} }
} }
} // I think that in the future we should perhaps have some sequence number system, i.e.
}; // so each request/response pair can be easily identified, so that if messages are
// not ordered (for some peculiar reason) we wouldn't lose anything.
// This would also require NOT discarding any text responses here.
let return_res = match ret { // TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
Err(err) => self.stream_return.send(Err(err)), Message::Text(text) => {
Ok(_) => { trace!(
self.packet_router.disarm();
task_client.disarm();
self.stream_return.send(Ok(split_stream))
}
};
if return_res.is_err() {
warn!("failed to return the split stream back on the oneshot channel")
}
}
fn handle_socket_messages(
&self,
msgs: Option<Vec<Result<Message, WsError>>>,
) -> Result<(), GatewayClientError> {
let ws_msgs = cleanup_socket_messages(msgs)?;
let plaintexts = self.recover_received_plaintexts(ws_msgs)?;
if !plaintexts.is_empty() {
self.packet_router.route_received(plaintexts)?
}
Ok(())
}
fn handle_binary_message(&self, binary_msg: Vec<u8>) -> Result<Vec<u8>, GatewayClientError> {
// this function decrypts the request and checks the MAC
match try_decrypt_binary_message(binary_msg, &self.shared_key) {
Some(plaintext) => Ok(plaintext),
None => {
error!("failed to decrypt and verify received message!");
Err(GatewayClientError::MalformedResponse)
}
}
}
// only returns an error on **critical** failures
fn handle_text_message(&self, text: String) -> Result<(), GatewayClientError> {
// if we fail to deserialise the response, return a hard error. we can't handle garbage
match ServerResponse::try_from(text).map_err(|_| GatewayClientError::MalformedResponse)? {
ServerResponse::Send {
remaining_bandwidth,
} => {
self.client_bandwidth
.update_and_maybe_log(remaining_bandwidth);
Ok(())
}
ServerResponse::Error { message } => {
error!("[1] gateway failure: {message}");
Err(GatewayClientError::GatewayError(message))
}
ServerResponse::TypedError { error } => {
match error {
SimpleGatewayRequestsError::OutOfBandwidth {
required,
available,
} => {
let available_bi2 = bibytes2(available as f64);
let required_bi2 = bibytes2(required as f64);
warn!("run out of bandwidth when attempting to send the message! we got {available_bi2} available, but needed at least {required_bi2} to send the previous message");
self.client_bandwidth.update_and_log(available);
// UNIMPLEMENTED: we should stop sending messages until we recover bandwidth
Ok(())
}
_ => {
error!("[2] gateway failure: {error}");
Err(GatewayClientError::TypedGatewayError(error))
}
}
}
other => {
let name = other.name();
warn!("received illegal message of type '{name}' in an authenticated client");
Ok(())
}
}
}
fn recover_received_plaintext(
&self,
message: Message,
) -> Result<Option<Vec<u8>>, GatewayClientError> {
match message {
Message::Binary(bin_msg) => {
let plaintext = self.handle_binary_message(bin_msg)?;
Ok(Some(plaintext))
}
// I think that in the future we should perhaps have some sequence number system, i.e.
// so each request/response pair can be easily identified, so that if messages are
// not ordered (for some peculiar reason) we wouldn't lose anything.
// This would also require NOT discarding any text responses here.
// TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
Message::Text(text) => {
trace!(
"received a text message - probably a response to some previous query! - {text}", "received a text message - probably a response to some previous query! - {text}",
); );
self.handle_text_message(text)?; match ServerResponse::try_from(text)
Ok(None) .map_err(|_| GatewayClientError::MalformedResponse)?
} {
_ => { ServerResponse::Send {
debug!("received websocket message that's neither 'Binary' nor 'Text'. it's going to get ignored"); remaining_bandwidth,
Ok(None) } => maybe_log_bandwidth(remaining_bandwidth),
} ServerResponse::Error { message } => {
} error!("gateway failure: {message}");
} return Err(GatewayClientError::GatewayError(message));
}
other => {
warn!(
"received illegal message of type {} in an authenticated client",
other.name()
)
}
}
fn recover_received_plaintexts( continue;
&self, }
messages: Vec<Message>, _ => continue,
) -> Result<Vec<Vec<u8>>, GatewayClientError> {
let mut plaintexts = Vec::new();
for ws_msg in messages {
if let Some(plaintext) = self.recover_received_plaintext(ws_msg)? {
plaintexts.push(plaintext)
} }
} }
Ok(plaintexts) Ok(plaintexts)
} }
fn spawn(self, split_stream: SplitStream<WsConn>, task_client: TaskClient) { fn route_socket_messages(
let fut = async move { self.run(split_stream, task_client).await }; ws_msgs: Vec<Message>,
packet_router: &PacketRouter,
#[cfg(target_arch = "wasm32")] shared_key: &SharedKeys,
wasm_bindgen_futures::spawn_local(fut); ) -> Result<(), GatewayClientError> {
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key)?;
#[cfg(not(target_arch = "wasm32"))] packet_router.route_received(plaintexts)
tokio::spawn(fut);
} }
}
impl PartiallyDelegatedHandle {
pub(crate) fn split_and_listen_for_mixnet_messages( pub(crate) fn split_and_listen_for_mixnet_messages(
conn: WsConn, conn: WsConn,
packet_router: PacketRouter, mut packet_router: PacketRouter,
shared_key: Arc<SharedKeys>, shared_key: Arc<SharedKeys>,
client_bandwidth: ClientBandwidth, mut shutdown: TaskClient,
shutdown: TaskClient,
) -> Self { ) -> Self {
// when called for, it NEEDS TO yield back the stream so that we could merge it and // when called for, it NEEDS TO yield back the stream so that we could merge it and
// read control request responses. // read control request responses.
@@ -257,18 +146,58 @@ impl PartiallyDelegatedHandle {
let (stream_sender, stream_receiver) = oneshot::channel(); let (stream_sender, stream_receiver) = oneshot::channel();
let ws_fd = ws_fd(&conn); let ws_fd = ws_fd(&conn);
let (sink, stream) = conn.split();
PartiallyDelegatedRouter::new( let (sink, mut stream) = conn.split();
packet_router,
shared_key,
client_bandwidth,
stream_sender,
notify_receiver,
)
.spawn(stream, shutdown);
PartiallyDelegatedHandle { let mixnet_receiver_future = async move {
let mut notify_receiver = notify_receiver;
let mut chunk_stream = (&mut stream).ready_chunks(8);
let ret_err = loop {
tokio::select! {
_ = shutdown.recv() => {
log::trace!("GatewayClient listener: Received shutdown");
log::debug!("GatewayClient listener: Exiting");
return;
}
_ = &mut notify_receiver => {
break Ok(());
}
msgs = chunk_stream.next() => {
let ws_msgs = match cleanup_socket_messages(msgs) {
Err(err) => break Err(err),
Ok(msgs) => msgs
};
if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) {
log::error!("Route socket messages failed: {err}");
break Err(err)
}
}
};
};
if match ret_err {
Err(err) => stream_sender.send(Err(err)),
Ok(_) => {
packet_router.mark_as_success();
shutdown.mark_as_success();
stream_sender.send(Ok(stream))
}
}
.is_err()
{
warn!("failed to send back `mixnet_receiver_future` result on the oneshot channel")
}
};
#[cfg(target_arch = "wasm32")]
wasm_bindgen_futures::spawn_local(mixnet_receiver_future);
#[cfg(not(target_arch = "wasm32"))]
tokio::spawn(mixnet_receiver_future);
PartiallyDelegated {
ws_fd, ws_fd,
sink_half: sink, sink_half: sink,
delegated_stream: (stream_receiver, notify_sender), delegated_stream: (stream_receiver, notify_sender),
@@ -337,7 +266,7 @@ impl PartiallyDelegatedHandle {
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum SocketState { pub(crate) enum SocketState {
Available(Box<WsConn>), Available(Box<WsConn>),
PartiallyDelegated(PartiallyDelegatedHandle), PartiallyDelegated(PartiallyDelegated),
NotConnected, NotConnected,
Invalid, Invalid,
} }
@@ -9,7 +9,7 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
base64 = { workspace = true } base64 = "0.13"
colored = { workspace = true } colored = { workspace = true }
nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" } nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
@@ -17,7 +17,6 @@ nym-contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common
nym-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-contract" } nym-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" } nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" }
nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" } nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-ecash-contract-common = { path = "../../cosmwasm-smart-contracts/ecash-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" } nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" } nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
@@ -27,10 +26,9 @@ thiserror = { workspace = true }
log = { workspace = true } log = { workspace = true }
url = { workspace = true, features = ["serde"] } url = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["sync", "time"] } tokio = { workspace = true, features = ["sync", "time"] }
time = { workspace = true, features = ["formatting"] }
futures = { workspace = true } futures = { workspace = true }
nym-compact-ecash = { path = "../../nym_offline_compact_ecash" } nym-coconut = { path = "../../nymcoconut" }
nym-network-defaults = { path = "../../network-defaults" } nym-network-defaults = { path = "../../network-defaults" }
nym-api-requests = { path = "../../../nym-api/nym-api-requests" } nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
@@ -90,6 +88,4 @@ default = ["http-client"]
http-client = ["cosmrs/rpc"] http-client = ["cosmrs/rpc"]
generate-ts = [] generate-ts = []
contract-testing = ["nym-mixnet-contract-common/contract-testing"] contract-testing = ["nym-mixnet-contract-common/contract-testing"]
# Features below are added to make clippy happy, it seems like they're unused we should remove them
tendermint-rpc-http-client = ["tendermint-rpc/http-client"]
tendermint-rpc-websocket-client = ["tendermint-rpc/websocket-client"]

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