Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7df17e3de | |||
| 9ddd56b2ca | |||
| 0b552d50bb | |||
| b143e755c4 | |||
| 6200899b70 | |||
| c82059fd06 | |||
| aea7607071 | |||
| d65096427b | |||
| 0d7e353c67 | |||
| 778c75bed1 | |||
| 915f0c971f | |||
| 6a30a1bf8f | |||
| a999c8886d | |||
| 9e5fa65a39 | |||
| 6b24600450 | |||
| 7621b00d53 | |||
| 162208b51c | |||
| 2f1f670db8 | |||
| 63ddadbc71 | |||
| cf1c1397db | |||
| 9b34e703ec | |||
| 1802188d15 | |||
| efbf03a170 | |||
| 8faea955f5 | |||
| c0811bc2a2 | |||
| 2ab7bc6e85 | |||
| b528750cdd | |||
| 712ad3579c | |||
| 3cf33000de | |||
| 8eb408e619 | |||
| e2a65b07e7 | |||
| 6e4ae07ac6 | |||
| 6868c7d5ae | |||
| 887db8c675 | |||
| ca3a022c62 | |||
| 99f71ff032 | |||
| 59d2518f63 | |||
| 8dc6ed9599 | |||
| d482c1a023 | |||
| b50173c56b | |||
| 2f70f0c9e5 | |||
| 24d7a5692d | |||
| 34e7f8dc09 | |||
| e206846d41 | |||
| 41bbceb753 | |||
| f2c0cd2861 | |||
| acfbb93acd | |||
| 6942cd5d2f | |||
| 809f602150 | |||
| 05318674af | |||
| 6d95fb2a39 | |||
| 515b3156a6 | |||
| 852f2f88e7 | |||
| fd221a97f5 | |||
| 837c28e6db | |||
| 1eb5d62fff | |||
| 1c7ce2d903 |
@@ -1,5 +1,2 @@
|
||||
nym-validator-rewarder/.sqlx/** diff=nodiff
|
||||
nym-node-status-api/nym-node-status-api/.sqlx/** diff=nodiff
|
||||
|
||||
# Use bd merge for beads JSONL files
|
||||
.beads/beads.jsonl merge=beads
|
||||
|
||||
@@ -25,7 +25,7 @@ Steps to reproduce the behaviour, if you're familiar with BDD syntax, please wri
|
||||
|
||||
*An example:*
|
||||
- Given I was setting up a mix-node following the instructions in the docs
|
||||
- And I successfully bonded my node via the wallet
|
||||
- And I successfully bonded my node via the the wallet
|
||||
- When I went to start my mixnode
|
||||
- Then I was presented with an error
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets --exclude nym-gateway-probe --exclude nym-node-status-api -- -D warnings
|
||||
args: --workspace --all-targets --exclude nym-gateway-probe -- -D warnings
|
||||
|
||||
- name: Clippy (non-macos)
|
||||
if: contains(matrix.os, 'linux') || contains(matrix.os, 'windows')
|
||||
@@ -104,14 +104,6 @@ jobs:
|
||||
with:
|
||||
command: build
|
||||
|
||||
# only build on linux because of wg FFI bindings of its dependency (network probe)
|
||||
- name: Build nym-node-status-api (linux only)
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: -p nym-node-status-api
|
||||
|
||||
- name: Build all examples
|
||||
if: contains(matrix.os, 'linux')
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -3,7 +3,7 @@ name: ci-check-ns-api-version
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "nym-node-status-api/nym-node-status-api/**"
|
||||
- "nym-node-status-api/**"
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
name: Publish to crates.io (dry run)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version to publish (e.g. 1.21.0)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
publish-dry-run:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Install cargo-workspaces
|
||||
run: cargo install cargo-workspaces
|
||||
|
||||
- name: Bump versions (local only)
|
||||
run: |
|
||||
cargo workspaces version ${{ inputs.version }} \
|
||||
--no-git-commit \
|
||||
--no-git-tag \
|
||||
--no-git-push \
|
||||
--yes
|
||||
|
||||
# Note: Dry run may show cascading dependency errors because packages
|
||||
# aren't actually uploaded. Check if the missing dependency has an
|
||||
# "aborting upload due to dry run" message earlier in the output - if so,
|
||||
# it would succeed in a real publish since cargo-workspaces publishes in
|
||||
# dependency order. cargo-workspaces doesn't fail on err, so there isn't
|
||||
# a good way to check this at the moment.
|
||||
- name: Publish (dry run)
|
||||
run: cargo workspaces publish --from-git --dry-run --allow-dirty
|
||||
@@ -1,47 +0,0 @@
|
||||
name: Publish to crates.io
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version to publish (e.g. 1.21.0)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Install cargo-workspaces
|
||||
run: cargo install cargo-workspaces
|
||||
|
||||
# - name: Configure git
|
||||
# run: |
|
||||
# git config user.name "github-actions[bot]"
|
||||
# git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Bump versions
|
||||
run: |
|
||||
cargo workspaces version ${{ inputs.version }} \
|
||||
--no-git-push \
|
||||
--no-git-tag \
|
||||
--yes
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
run: cargo workspaces publish --from-git --no-git-commit
|
||||
|
||||
# - name: Push version commit
|
||||
# run: |
|
||||
# git push origin HEAD
|
||||
-12
@@ -65,15 +65,3 @@ nym-api/redocly/formatted-openapi.json
|
||||
**/enter_db.sh
|
||||
|
||||
*.profraw
|
||||
.beads
|
||||
CLAUDE.md
|
||||
docs
|
||||
.claude
|
||||
.superego
|
||||
|
||||
# Superego (machine-specific paths)
|
||||
.superego/
|
||||
.claude/hooks/superego/
|
||||
.claude/settings.json
|
||||
|
||||
/notes
|
||||
|
||||
-138
@@ -4,144 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2026.2-oscypek] (2026-01-27)
|
||||
|
||||
- bugfix: downgrade gateway protocol to clients proposed version ([#6377])
|
||||
- bugfix: ack fix ([#6364])
|
||||
- Cherry pick/api urls oscypek ([#6348])
|
||||
- Update nix to v0.30.1 ([#6316])
|
||||
- Deriving Serialize for GatewayData ([#6314])
|
||||
- chore: remove repetitive words in comment ([#6313])
|
||||
- [bugfix] Sqlite transaction escalation was causing errors ([#6299])
|
||||
- DNS static table pre-resolve ([#6297])
|
||||
- Add Copy+Clone to nym_api_provider::Config ([#6296])
|
||||
- [chore] clippy fixes and use fixed rust version from REQUIRED_RUSTC_VERSION ([#6295])
|
||||
- build(deps): bump SonarSource/sonarqube-scan-action from 6 to 7 ([#6294])
|
||||
- build(deps): bump mikefarah/yq from 4.49.2 to 4.50.1 ([#6293])
|
||||
- build(deps): bump actions/upload-artifact from 5 to 6 ([#6292])
|
||||
- build(deps): bump actions/download-artifact from 6 to 7 ([#6291])
|
||||
- build(deps): bump js-yaml from 3.14.1 to 3.14.2 in /documentation/docs ([#6290])
|
||||
- build(deps): bump next from 15.4.9 to 15.4.10 in /nym-node-status-api/nym-node-status-ui ([#6289])
|
||||
- build(deps): bump next from 14.2.33 to 14.2.35 ([#6288])
|
||||
- LP Registration + Telescoping + Gateway Probe Localnet Mode ([#6286])
|
||||
- build(deps): bump next from 15.5.7 to 15.5.9 in /documentation/docs ([#6285])
|
||||
- build(deps): bump next from 15.4.7 to 15.4.9 in /nym-node-status-api/nym-node-status-ui ([#6284])
|
||||
- Minor DNS improvements ([#6283])
|
||||
- HTTP client without default features ([#6281])
|
||||
- DNS: reduce number of attempts ([#6278])
|
||||
- [bugfix] use proper mixing delay instead of poisson delay in cover traffic ([#6269])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.3 in /wasm/zknym-lib/internal-dev ([#6261])
|
||||
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.3 in /wasm/mix-fetch/internal-dev ([#6260])
|
||||
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.2 in /wasm/client/internal-dev ([#6251])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.2 in /nym-credential-proxy/vpn-api-lib-wasm/internal-dev ([#6250])
|
||||
- [Feature] Fallback gateway listener and remove legacy key support ([#6249])
|
||||
- build(deps-dev): bump node-forge from 1.3.0 to 1.3.2 in /clients/native/examples/js-examples/websocket ([#6248])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.2 ([#6246])
|
||||
- build(deps): bump pnpm/action-setup from 4.1.0 to 4.2.0 ([#6245])
|
||||
- build(deps): bump actions/download-artifact from 5 to 6 ([#6244])
|
||||
- build(deps): bump actions/checkout from 4 to 6 ([#6243])
|
||||
- build(deps): bump mikefarah/yq from 4.48.1 to 4.49.2 ([#6242])
|
||||
- build(deps): bump actions/upload-artifact from 4 to 5 ([#6241])
|
||||
- fix: fix assertion ([#6238])
|
||||
- Initial changes to support extra configurable parameters and to print… ([#6237])
|
||||
- Data Observatory ([#6172])
|
||||
|
||||
[#6377]: https://github.com/nymtech/nym/pull/6377
|
||||
[#6364]: https://github.com/nymtech/nym/pull/6364
|
||||
[#6348]: https://github.com/nymtech/nym/pull/6348
|
||||
[#6316]: https://github.com/nymtech/nym/pull/6316
|
||||
[#6314]: https://github.com/nymtech/nym/pull/6314
|
||||
[#6313]: https://github.com/nymtech/nym/pull/6313
|
||||
[#6299]: https://github.com/nymtech/nym/pull/6299
|
||||
[#6297]: https://github.com/nymtech/nym/pull/6297
|
||||
[#6296]: https://github.com/nymtech/nym/pull/6296
|
||||
[#6295]: https://github.com/nymtech/nym/pull/6295
|
||||
[#6294]: https://github.com/nymtech/nym/pull/6294
|
||||
[#6293]: https://github.com/nymtech/nym/pull/6293
|
||||
[#6292]: https://github.com/nymtech/nym/pull/6292
|
||||
[#6291]: https://github.com/nymtech/nym/pull/6291
|
||||
[#6290]: https://github.com/nymtech/nym/pull/6290
|
||||
[#6289]: https://github.com/nymtech/nym/pull/6289
|
||||
[#6288]: https://github.com/nymtech/nym/pull/6288
|
||||
[#6286]: https://github.com/nymtech/nym/pull/6286
|
||||
[#6285]: https://github.com/nymtech/nym/pull/6285
|
||||
[#6284]: https://github.com/nymtech/nym/pull/6284
|
||||
[#6283]: https://github.com/nymtech/nym/pull/6283
|
||||
[#6281]: https://github.com/nymtech/nym/pull/6281
|
||||
[#6278]: https://github.com/nymtech/nym/pull/6278
|
||||
[#6269]: https://github.com/nymtech/nym/pull/6269
|
||||
[#6261]: https://github.com/nymtech/nym/pull/6261
|
||||
[#6260]: https://github.com/nymtech/nym/pull/6260
|
||||
[#6251]: https://github.com/nymtech/nym/pull/6251
|
||||
[#6250]: https://github.com/nymtech/nym/pull/6250
|
||||
[#6249]: https://github.com/nymtech/nym/pull/6249
|
||||
[#6248]: https://github.com/nymtech/nym/pull/6248
|
||||
[#6246]: https://github.com/nymtech/nym/pull/6246
|
||||
[#6245]: https://github.com/nymtech/nym/pull/6245
|
||||
[#6244]: https://github.com/nymtech/nym/pull/6244
|
||||
[#6243]: https://github.com/nymtech/nym/pull/6243
|
||||
[#6242]: https://github.com/nymtech/nym/pull/6242
|
||||
[#6241]: https://github.com/nymtech/nym/pull/6241
|
||||
[#6238]: https://github.com/nymtech/nym/pull/6238
|
||||
[#6237]: https://github.com/nymtech/nym/pull/6237
|
||||
[#6172]: https://github.com/nymtech/nym/pull/6172
|
||||
|
||||
## [2026.1-niolo] (2026-01-13)
|
||||
|
||||
- bugfix: mozzarella -> niolo config migration ([#6259])
|
||||
- chore: remove run DKG migration ([#6253])
|
||||
- bugfix: reexposed 'derive_extended_private_key' ([#6247])
|
||||
- Bump js-yaml from 3.14.1 to 3.14.2 in /sdk/typescript/codegen/contract-clients ([#6231])
|
||||
- Statistics API v2 ([#6227])
|
||||
- Bump golang.org/x/crypto from 0.39.0 to 0.45.0 in /nym-gateway-probe/netstack_ping ([#6220])
|
||||
- Update chain registry link ([#6219])
|
||||
- Bump glob from 10.3.4 to 10.5.0 in /documentation/scripts/post-process ([#6216])
|
||||
- Bump js-yaml from 4.1.0 to 4.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6215])
|
||||
- gateway-probe fixes for run-local ([#6212])
|
||||
- chore: updated default endpoint for retrieving attestation.json ([#6207])
|
||||
- chore: remove support for legacy mixnode within the performance contract ([#6205])
|
||||
- feat: upgrade mode: VPN adjustments ([#6189])
|
||||
- Bump min-document from 2.19.0 to 2.19.1 ([#6181])
|
||||
- Bump next from 15.4.1 to 15.4.7 in /nym-node-status-api/nym-node-status-ui ([#6180])
|
||||
- feat: merge intermediate upgrade mode changes ([#6174])
|
||||
- Add weighted scoring to NS API ([#6144])
|
||||
- build(deps): bump mikefarah/yq from 4.47.1 to 4.48.1 ([#6107])
|
||||
- build(deps): bump SonarSource/sonarqube-scan-action from 5 to 6 in /.github/workflows ([#6068])
|
||||
- build(deps): bump tar-fs from 3.0.9 to 3.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6063])
|
||||
- build(deps): bump ammonia from 4.1.1 to 4.1.2 ([#6057])
|
||||
- build(deps): bump tower-http from 0.5.2 to 0.6.6 ([#6030])
|
||||
- build(deps): bump actions/setup-go from 5 to 6 ([#6013])
|
||||
- build(deps): bump next from 14.2.28 to 14.2.32 ([#5996])
|
||||
- build(deps): bump tracing-subscriber from 0.3.19 to 0.3.20 ([#5993])
|
||||
- build(deps): bump actions/upload-pages-artifact from 3 to 4 ([#5992])
|
||||
|
||||
[#6259]: https://github.com/nymtech/nym/pull/6259
|
||||
[#6253]: https://github.com/nymtech/nym/pull/6253
|
||||
[#6247]: https://github.com/nymtech/nym/pull/6247
|
||||
[#6231]: https://github.com/nymtech/nym/pull/6231
|
||||
[#6227]: https://github.com/nymtech/nym/pull/6227
|
||||
[#6220]: https://github.com/nymtech/nym/pull/6220
|
||||
[#6219]: https://github.com/nymtech/nym/pull/6219
|
||||
[#6216]: https://github.com/nymtech/nym/pull/6216
|
||||
[#6215]: https://github.com/nymtech/nym/pull/6215
|
||||
[#6212]: https://github.com/nymtech/nym/pull/6212
|
||||
[#6207]: https://github.com/nymtech/nym/pull/6207
|
||||
[#6205]: https://github.com/nymtech/nym/pull/6205
|
||||
[#6189]: https://github.com/nymtech/nym/pull/6189
|
||||
[#6181]: https://github.com/nymtech/nym/pull/6181
|
||||
[#6180]: https://github.com/nymtech/nym/pull/6180
|
||||
[#6174]: https://github.com/nymtech/nym/pull/6174
|
||||
[#6144]: https://github.com/nymtech/nym/pull/6144
|
||||
[#6107]: https://github.com/nymtech/nym/pull/6107
|
||||
[#6068]: https://github.com/nymtech/nym/pull/6068
|
||||
[#6063]: https://github.com/nymtech/nym/pull/6063
|
||||
[#6057]: https://github.com/nymtech/nym/pull/6057
|
||||
[#6030]: https://github.com/nymtech/nym/pull/6030
|
||||
[#6013]: https://github.com/nymtech/nym/pull/6013
|
||||
[#5996]: https://github.com/nymtech/nym/pull/5996
|
||||
[#5993]: https://github.com/nymtech/nym/pull/5993
|
||||
[#5992]: https://github.com/nymtech/nym/pull/5992
|
||||
|
||||
## [2025.21-mozzarella] (2025-11-25)
|
||||
|
||||
- [bugfix] Tunnel not waiting on MixnetClient to shut down cleanly ([#6225])
|
||||
|
||||
@@ -0,0 +1,686 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Nym is a privacy platform that uses mixnet technology to protect against metadata surveillance. The platform consists of several key components:
|
||||
- Mixnet nodes (mixnodes) for packet mixing
|
||||
- Gateways (entry/exit points for the network)
|
||||
- Clients for interacting with the network
|
||||
- Network monitoring tools
|
||||
- Validators for network consensus
|
||||
- Various service providers and integrations
|
||||
|
||||
## Build Commands
|
||||
|
||||
### Rust Components
|
||||
|
||||
```bash
|
||||
# Default build (debug)
|
||||
cargo build
|
||||
|
||||
# Release build
|
||||
cargo build --release
|
||||
|
||||
# Build a specific package
|
||||
cargo build -p <package-name>
|
||||
|
||||
# Build main components
|
||||
make build
|
||||
|
||||
# Build release versions of main binaries and contracts
|
||||
make build-release
|
||||
|
||||
# Build specific binaries
|
||||
make build-nym-cli
|
||||
cargo build -p nym-node --release
|
||||
cargo build -p nym-api --release
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Run clippy, unit tests, and formatting
|
||||
make test
|
||||
|
||||
# Run all tests including slow tests
|
||||
make test-all
|
||||
|
||||
# Run clippy on all workspaces
|
||||
make clippy
|
||||
|
||||
# Run unit tests for a specific package
|
||||
cargo test -p <package-name>
|
||||
|
||||
# Run only expensive/ignored tests
|
||||
cargo test --workspace -- --ignored
|
||||
|
||||
# Run API tests
|
||||
dotenv -f envs/sandbox.env -- cargo test --test public-api-tests
|
||||
|
||||
# Run tests with specific log level
|
||||
RUST_LOG=debug cargo test -p <package-name>
|
||||
|
||||
# Run specific test scripts
|
||||
./nym-node/tests/test_apis.sh
|
||||
./scripts/wireguard-exit-policy/exit-policy-tests.sh
|
||||
```
|
||||
|
||||
### Linting and Formatting
|
||||
|
||||
```bash
|
||||
# Run rustfmt on all code
|
||||
make fmt
|
||||
|
||||
# Check formatting without modifying
|
||||
cargo fmt --all -- --check
|
||||
|
||||
# Run clippy with all targets
|
||||
cargo clippy --workspace --all-targets -- -D warnings
|
||||
|
||||
# TypeScript linting
|
||||
yarn lint
|
||||
yarn lint:fix
|
||||
yarn types:lint:fix
|
||||
|
||||
# Check dependencies for security/licensing issues
|
||||
cargo deny check
|
||||
```
|
||||
|
||||
### WASM Components
|
||||
|
||||
```bash
|
||||
# Build all WASM components
|
||||
make sdk-wasm-build
|
||||
|
||||
# Build TypeScript SDK
|
||||
yarn build:sdk
|
||||
npx lerna run --scope @nymproject/sdk build --stream
|
||||
|
||||
# Build and test WASM components
|
||||
make sdk-wasm
|
||||
|
||||
# Build specific WASM packages
|
||||
cd wasm/client && make
|
||||
cd wasm/mix-fetch && make
|
||||
cd wasm/node-tester && make
|
||||
```
|
||||
|
||||
### Contract Development
|
||||
|
||||
```bash
|
||||
# Build all contracts
|
||||
make contracts
|
||||
|
||||
# Build contracts in release mode
|
||||
make build-release-contracts
|
||||
|
||||
# Generate contract schemas
|
||||
make contract-schema
|
||||
|
||||
# Run wasm-opt on contracts
|
||||
make wasm-opt-contracts
|
||||
|
||||
# Check contracts with cosmwasm-check
|
||||
make cosmwasm-check-contracts
|
||||
```
|
||||
|
||||
### Running Components
|
||||
|
||||
```bash
|
||||
# Run nym-node as a mixnode
|
||||
cargo run -p nym-node -- run --mode mixnode
|
||||
|
||||
# Run nym-node as a gateway
|
||||
cargo run -p nym-node -- run --mode gateway
|
||||
|
||||
# Run the network monitor
|
||||
cargo run -p nym-network-monitor
|
||||
|
||||
# Run the API server
|
||||
cargo run -p nym-api
|
||||
|
||||
# Run with specific environment
|
||||
dotenv -f envs/sandbox.env -- cargo run -p nym-api
|
||||
|
||||
# Start a local network
|
||||
./scripts/localnet_start.sh
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The Nym platform consists of various components organized as a monorepo:
|
||||
|
||||
1. **Core Mixnet Infrastructure**:
|
||||
- `nym-node`: Core binary supporting mixnode and gateway modes
|
||||
- `common/nymsphinx`: Implementation of the Sphinx packet format
|
||||
- `common/topology`: Network topology management
|
||||
- `common/types`: Shared data types across components
|
||||
|
||||
2. **Network Monitoring**:
|
||||
- `nym-network-monitor`: Monitors the network's reliability and performance
|
||||
- `nym-api`: API server for network stats and monitoring data
|
||||
- Metrics tracking for nodes, routes, and overall network health
|
||||
|
||||
3. **Client Implementations**:
|
||||
- `clients/native`: Native Rust client implementation
|
||||
- `clients/socks5`: SOCKS5 proxy client for standard applications
|
||||
- `wasm`: WebAssembly client implementations (for browsers)
|
||||
- `nym-connect`: Desktop and mobile clients
|
||||
|
||||
4. **Blockchain & Smart Contracts**:
|
||||
- `common/cosmwasm-smart-contracts`: Smart contract implementations
|
||||
- `contracts`: CosmWasm contracts for the Nym network
|
||||
- `common/ledger`: Blockchain integration
|
||||
|
||||
5. **Utilities & Tools**:
|
||||
- `tools`: Various CLI tools and utilities
|
||||
- `sdk`: SDKs for different languages and platforms
|
||||
- `documentation`: Documentation generation and management
|
||||
|
||||
## Packet System
|
||||
|
||||
Nym uses a modified Sphinx packet format for its mixnet:
|
||||
|
||||
1. **Message Chunking**:
|
||||
- Messages are divided into "sets" and "fragments"
|
||||
- Each fragment fits in a single Sphinx packet
|
||||
- The `common/nymsphinx/chunking` module handles message fragmentation
|
||||
|
||||
2. **Routing**:
|
||||
- Packets traverse through 3 layers of mixnodes
|
||||
- Routing information is encrypted in layers (onion routing)
|
||||
- The final gateway receives and processes the messages
|
||||
|
||||
3. **Monitoring**:
|
||||
- Monitoring system tracks packet delivery through the network
|
||||
- Routes are analyzed for reliability statistics
|
||||
- Node performance metrics are collected
|
||||
|
||||
## Network Protocol
|
||||
|
||||
Nym implements the Loopix mixnet design with several key privacy features:
|
||||
|
||||
1. **Continuous-time Mixing**:
|
||||
- Each mixnode delays messages independently with an exponential distribution
|
||||
- This creates random reordering of packets, destroying timing correlations
|
||||
- Offers better anonymity properties than batch mixing approaches
|
||||
|
||||
2. **Cover Traffic**:
|
||||
- Clients and nodes generate dummy "loop" packets that circulate through the network
|
||||
- These packets are indistinguishable from real traffic
|
||||
- Creates a baseline level of traffic that hides actual communication patterns
|
||||
- Provides unobservability (hiding when and how much real traffic is being sent)
|
||||
|
||||
3. **Stratified Network Architecture**:
|
||||
- Traffic flows through Entry Gateway → 3 Mixnode Layers → Exit Gateway
|
||||
- Path selection is independent per-message (unlike Tor)
|
||||
- Each node connects only to adjacent layers
|
||||
|
||||
4. **Anonymous Replies**:
|
||||
- Single-Use Reply Blocks (SURBs) allow receiving messages without revealing identity
|
||||
- Enables bidirectional communication while maintaining privacy
|
||||
|
||||
## Network Monitoring Architecture
|
||||
|
||||
The network monitoring system is a core component that measures mixnet reliability:
|
||||
|
||||
1. The `nym-network-monitor` sends test packets through the network
|
||||
2. These packets follow predefined routes through multiple mixnodes
|
||||
3. Metrics are collected about:
|
||||
- Successful and failed packet deliveries
|
||||
- Node reliability (percentage of successful packet handling)
|
||||
- Route reliability (which specific route combinations work best)
|
||||
4. Results are stored in the database and used by `nym-api` to:
|
||||
- Present node performance statistics
|
||||
- Determine network rewards
|
||||
- Provide route selection guidance to clients
|
||||
|
||||
In the current branch, metrics collection is being enhanced with a fanout approach to submit to multiple API endpoints.
|
||||
|
||||
## Development Environment
|
||||
|
||||
### Required Dependencies
|
||||
|
||||
- Rust toolchain (stable, 1.80+)
|
||||
- Node.js (v20+) and yarn for TypeScript components
|
||||
- SQLite for local database development
|
||||
- PostgreSQL for API database (optional, for full API functionality)
|
||||
- CosmWasm tools for contract development
|
||||
- For building contracts: `wasm-opt` tool from `binaryen`
|
||||
- Python 3.8+ for some scripts
|
||||
- Docker (optional, for containerized development)
|
||||
- protoc (Protocol Buffers compiler) for some components
|
||||
|
||||
### Environment Configurations
|
||||
|
||||
The `envs/` directory contains pre-configured environments:
|
||||
|
||||
#### Available Environments
|
||||
|
||||
- **`local.env`**: Local development environment
|
||||
- Points to local services (localhost)
|
||||
- Uses test mnemonics and keys
|
||||
- Ideal for testing without external dependencies
|
||||
|
||||
- **`sandbox.env`**: Sandbox test network
|
||||
- Public test network with real nodes
|
||||
- Test tokens available from faucet
|
||||
- Contract addresses for sandbox deployment
|
||||
- API: https://sandbox-nym-api1.nymtech.net
|
||||
|
||||
- **`mainnet.env`**: Production mainnet
|
||||
- Real network with real tokens
|
||||
- Production contract addresses
|
||||
- API: https://validator.nymtech.net
|
||||
- Use with caution!
|
||||
|
||||
- **`canary.env`**: Canary deployment
|
||||
- Pre-release testing environment
|
||||
- Tests new features before mainnet
|
||||
|
||||
- **`mainnet-local-api.env`**: Hybrid environment
|
||||
- Uses mainnet contracts but local API
|
||||
- Useful for API development against mainnet data
|
||||
|
||||
#### Key Environment Variables
|
||||
|
||||
```bash
|
||||
# Network configuration
|
||||
NETWORK_NAME=sandbox # Network identifier
|
||||
BECH32_PREFIX=n # Address prefix (n for sandbox, n for mainnet)
|
||||
NYM_API=https://sandbox-nym-api1.nymtech.net/api
|
||||
NYXD=https://rpc.sandbox.nymtech.net
|
||||
NYM_API_NETWORK=sandbox
|
||||
|
||||
# Contract addresses (network-specific)
|
||||
MIXNET_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
|
||||
VESTING_CONTRACT_ADDRESS=n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz
|
||||
# ... other contract addresses
|
||||
|
||||
# Mnemonic for testing (NEVER use in production)
|
||||
MNEMONIC="clutch captain shoe salt awake harvest setup primary inmate ugly among become"
|
||||
|
||||
# API Keys and tokens
|
||||
IPINFO_API_TOKEN=your_token_here
|
||||
AUTHENTICATOR_PASSWORD=password_here
|
||||
|
||||
# Logging
|
||||
RUST_LOG=info # Options: error, warn, info, debug, trace
|
||||
RUST_BACKTRACE=1 # Enable backtraces
|
||||
|
||||
# Database
|
||||
DATABASE_URL=postgresql://user:pass@localhost/nym_api
|
||||
```
|
||||
|
||||
#### Using Environment Files
|
||||
|
||||
```bash
|
||||
# Load environment and run command
|
||||
dotenv -f envs/sandbox.env -- cargo run -p nym-api
|
||||
|
||||
# Export to shell
|
||||
source envs/sandbox.env
|
||||
|
||||
# Use with make targets
|
||||
dotenv -f envs/sandbox.env -- make run-api-tests
|
||||
```
|
||||
|
||||
## Initial Setup
|
||||
|
||||
### First Time Setup
|
||||
|
||||
1. **Install Prerequisites**
|
||||
```bash
|
||||
# Install Rust
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
|
||||
# Install Node.js and yarn
|
||||
# Via nvm (recommended):
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
|
||||
nvm install 20
|
||||
npm install -g yarn
|
||||
|
||||
# Install build tools
|
||||
# Ubuntu/Debian:
|
||||
sudo apt-get install build-essential pkg-config libssl-dev protobuf-compiler libpq-dev
|
||||
|
||||
# macOS:
|
||||
brew install protobuf postgresql
|
||||
|
||||
# Install wasm-opt for contract builds
|
||||
npm install -g wasm-opt
|
||||
|
||||
# Add wasm target for Rust
|
||||
rustup target add wasm32-unknown-unknown
|
||||
```
|
||||
|
||||
2. **Clone and Setup Repository**
|
||||
```bash
|
||||
git clone https://github.com/nymtech/nym.git
|
||||
cd nym/nym
|
||||
|
||||
# Install JavaScript dependencies
|
||||
yarn install
|
||||
|
||||
# Build the project
|
||||
make build
|
||||
```
|
||||
|
||||
3. **Database Setup (Optional, for API development)**
|
||||
```bash
|
||||
# Install PostgreSQL
|
||||
# Create database
|
||||
createdb nym_api
|
||||
|
||||
# Run migrations (from nym-api directory)
|
||||
cd nym-api
|
||||
sqlx migrate run
|
||||
```
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# Run a mixnode locally
|
||||
dotenv -f envs/sandbox.env -- cargo run -p nym-node -- run --mode mixnode --id my-mixnode
|
||||
|
||||
# Run a gateway locally
|
||||
dotenv -f envs/sandbox.env -- cargo run -p nym-node -- run --mode gateway --id my-gateway
|
||||
|
||||
# Run the API server
|
||||
dotenv -f envs/sandbox.env -- cargo run -p nym-api
|
||||
|
||||
# Run a client
|
||||
cargo run -p nym-client -- init --id my-client
|
||||
cargo run -p nym-client -- run --id my-client
|
||||
```
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
The project uses GitHub Actions for CI/CD with several key workflows:
|
||||
|
||||
1. **Build and Test**:
|
||||
- `ci-build.yml`: Main build workflow for Rust components
|
||||
- Tests are run on multiple platforms (Linux, Windows, macOS)
|
||||
- Includes formatting check (rustfmt) and linting (clippy)
|
||||
|
||||
2. **Release Process**:
|
||||
- Binary artifacts are published on release tags
|
||||
- Multiple platform builds are created
|
||||
|
||||
3. **Documentation**:
|
||||
- Documentation is automatically built and deployed
|
||||
|
||||
## Database Structure
|
||||
|
||||
The system uses SQLite databases with tables like:
|
||||
- `mixnode_status`: Status information about mixnodes
|
||||
- `gateway_status`: Status information about gateways
|
||||
- `routes`: Route performance information (success/failure of specific paths)
|
||||
- `monitor_run`: Information about monitoring test runs
|
||||
|
||||
## Development Workflows
|
||||
|
||||
### Running a Node
|
||||
|
||||
To run the mixnode or gateway:
|
||||
|
||||
```bash
|
||||
# Run nym-node as a mixnode with specified identity
|
||||
cargo run -p nym-node -- run --mode mixnode --id my-mixnode
|
||||
|
||||
# Run nym-node as a gateway
|
||||
cargo run -p nym-node -- run --mode gateway --id my-gateway
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Nodes can be configured with files in various locations:
|
||||
- Command-line arguments
|
||||
- Environment variables
|
||||
- `.env` files specified with `--config-env-file`
|
||||
|
||||
### Monitoring
|
||||
|
||||
To monitor the health of your node:
|
||||
- View logs for real-time information
|
||||
- Use the node's HTTP API for status information
|
||||
- Check the explorer for public node statistics
|
||||
|
||||
## Common Libraries
|
||||
|
||||
- `common/types`: Shared data types across all components
|
||||
- `common/crypto`: Cryptographic primitives and wrappers
|
||||
- `common/client-core`: Core client functionality
|
||||
- `common/gateway-client`: Client-gateway communication
|
||||
- `common/task`: Task management and concurrency utilities
|
||||
- `common/nymsphinx`: Sphinx packet implementation for mixnet
|
||||
- `common/topology`: Network topology management
|
||||
- `common/credentials`: Credential system for privacy-preserving authentication
|
||||
- `common/bandwidth-controller`: Bandwidth management and accounting
|
||||
|
||||
## Code Conventions
|
||||
|
||||
- Error handling: Use anyhow/thiserror for structured error handling
|
||||
- Logging: Use the tracing framework for logging and diagnostics
|
||||
- State management: Generally use Tokio/futures for async code
|
||||
- Configuration: Use the config crate and env vars with defaults
|
||||
- Database: Use sqlx for type-safe database queries
|
||||
- Follow clippy recommendations and rustfmt formatting
|
||||
- Use semantic commit messages: feat, fix, docs, refactor, test, chore
|
||||
|
||||
## When Making Changes
|
||||
|
||||
- Run `make test` before submitting PRs
|
||||
- Follow Rust naming conventions
|
||||
- Use `clippy` to check for common issues
|
||||
- Update SQLx query caches when modifying DB queries: `cargo sqlx prepare`
|
||||
- Consider backward compatibility for protocol changes
|
||||
- Use lefthook pre-commit hooks for TypeScript formatting
|
||||
- Run `cargo deny check` to verify dependency compliance
|
||||
- Test against both sandbox and local environments when possible
|
||||
- Update relevant documentation and CHANGELOG.md
|
||||
|
||||
## Development Tools
|
||||
|
||||
### Useful Cargo Commands
|
||||
|
||||
```bash
|
||||
# Check for outdated dependencies
|
||||
cargo outdated
|
||||
|
||||
# Analyze binary size
|
||||
cargo bloat --release -p nym-node
|
||||
|
||||
# Generate dependency graph
|
||||
cargo tree -p nym-api
|
||||
|
||||
# Run with instrumentation
|
||||
cargo run --features profiling -p nym-node
|
||||
|
||||
# Check for security advisories
|
||||
cargo audit
|
||||
```
|
||||
|
||||
### Database Tools
|
||||
|
||||
```bash
|
||||
# SQLx CLI for migrations
|
||||
cargo install sqlx-cli
|
||||
|
||||
# Create new migration
|
||||
cd nym-api && sqlx migrate add <migration_name>
|
||||
|
||||
# Prepare query metadata for offline compilation
|
||||
cargo sqlx prepare --workspace
|
||||
|
||||
# View database schema
|
||||
./nym-api/enter_db.sh
|
||||
```
|
||||
|
||||
### Development Scripts
|
||||
|
||||
- `scripts/build_topology.py`: Generate network topology files
|
||||
- `scripts/node_api_check.py`: Verify node API endpoints
|
||||
- `scripts/network_tunnel_manager.sh`: Manage network tunnels
|
||||
- `scripts/localnet_start.sh`: Start a local test network
|
||||
- Various deployment scripts in `deployment/` for different environments
|
||||
|
||||
## Debugging
|
||||
|
||||
- Enable more verbose logging with the RUST_LOG environment variable:
|
||||
```
|
||||
RUST_LOG=debug,nym_node=trace cargo run -p nym-node -- run --mode mixnode
|
||||
```
|
||||
- Use the HTTP API endpoints for status information
|
||||
- Check monitoring data in the database for network performance metrics
|
||||
- For complex issues, use tracing tools to follow packet flow
|
||||
- Enable backtraces: `RUST_BACKTRACE=full`
|
||||
- For WASM debugging: Use browser developer tools with source maps
|
||||
|
||||
## Deployment and Advanced Configurations
|
||||
|
||||
### Deployment Structure
|
||||
|
||||
The `deployment/` directory contains Ansible playbooks and configurations for various deployment scenarios:
|
||||
|
||||
- **`aws/`**: AWS-specific deployment configurations
|
||||
- **`mixnode/`**: Mixnode deployment playbooks
|
||||
- **`gateway/`**: Gateway deployment playbooks
|
||||
- **`validator/`**: Validator node deployment
|
||||
- **`sandbox-v2/`**: Complete sandbox environment setup
|
||||
- **`big-dipper-2/`**: Block explorer deployment
|
||||
|
||||
### Sandbox V2 Deployment
|
||||
|
||||
The sandbox-v2 deployment (`deployment/sandbox-v2/`) provides a complete test environment:
|
||||
|
||||
```bash
|
||||
# Key playbooks:
|
||||
- deploy.yaml # Main deployment orchestrator
|
||||
- deploy-mixnodes.yaml # Deploy mixnodes
|
||||
- deploy-gateways.yaml # Deploy gateways
|
||||
- deploy-validators.yaml # Deploy validator nodes
|
||||
- deploy-nym-api.yaml # Deploy API services
|
||||
```
|
||||
|
||||
### Custom Environment Setup
|
||||
|
||||
To create a custom environment:
|
||||
|
||||
1. Copy an existing env file: `cp envs/sandbox.env envs/custom.env`
|
||||
2. Modify the network endpoints and contract addresses
|
||||
3. Update the `NETWORK_NAME` to your identifier
|
||||
4. Set appropriate mnemonics and keys (use fresh ones for production!)
|
||||
|
||||
### Contract Addresses
|
||||
|
||||
Contract addresses are network-specific and defined in environment files:
|
||||
- Mixnet contract: Manages mixnode/gateway registry
|
||||
- Vesting contract: Handles token vesting schedules
|
||||
- Coconut contracts: Privacy-preserving credentials
|
||||
- Name service: Human-readable address mapping
|
||||
- Ecash contract: Electronic cash functionality
|
||||
|
||||
### Local Network Setup
|
||||
|
||||
For a completely local network:
|
||||
```bash
|
||||
# Start local chain
|
||||
./scripts/localnet_start.sh
|
||||
|
||||
# Deploy contracts
|
||||
cd contracts
|
||||
make deploy-local
|
||||
|
||||
# Start nodes with local config
|
||||
dotenv -f envs/local.env -- cargo run -p nym-node -- run --mode mixnode
|
||||
```
|
||||
|
||||
## Common Issues and Troubleshooting
|
||||
|
||||
### Database Issues
|
||||
|
||||
- When modifying database queries, you must update SQLx query caches:
|
||||
```bash
|
||||
cargo sqlx prepare
|
||||
```
|
||||
- If you see SQLx errors about missing query files, this is likely the cause
|
||||
- For "database is locked" errors with SQLite, ensure only one process accesses the DB
|
||||
- For PostgreSQL connection issues, verify DATABASE_URL and that the server is running
|
||||
|
||||
### API Connection Issues
|
||||
|
||||
- Check the environment variables pointing to the APIs (NYM_API, NYXD)
|
||||
- Verify network connectivity and API health endpoints
|
||||
- For authentication issues, check node keys and credentials
|
||||
- Common endpoints to verify:
|
||||
- API health: `$NYM_API/health`
|
||||
- Chain status: `$NYXD/status`
|
||||
- Contract info: `$NYXD/cosmwasm/wasm/v1/contract/$CONTRACT_ADDRESS`
|
||||
|
||||
### Build Problems
|
||||
|
||||
- Clean dependencies with `cargo clean` for a fresh build
|
||||
- Check for compatible Rust version (1.80+ recommended)
|
||||
- For smart contract builds, ensure wasm-opt is installed: `npm install -g wasm-opt`
|
||||
- For cross-compilation issues, check target-specific dependencies
|
||||
- WASM build issues: Ensure wasm32-unknown-unknown target is installed:
|
||||
```bash
|
||||
rustup target add wasm32-unknown-unknown
|
||||
```
|
||||
- For "cannot find -lpq" errors, install PostgreSQL development files:
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get install libpq-dev
|
||||
# macOS
|
||||
brew install postgresql
|
||||
```
|
||||
|
||||
### Environment Issues
|
||||
|
||||
- Contract address mismatches: Ensure you're using the correct environment file
|
||||
- "Account sequence mismatch": The account nonce is out of sync, wait and retry
|
||||
- Token decimal issues: Sandbox uses different decimal places than mainnet
|
||||
- API version mismatches: Ensure your local API version matches the network
|
||||
- "Insufficient funds": Get test tokens from faucet (sandbox) or check balance
|
||||
- Gateway/mixnode bonding issues: Verify minimum stake requirements
|
||||
|
||||
## Working with Routes and Monitoring
|
||||
|
||||
1. Route monitoring metrics are stored in a `routes` table with:
|
||||
- Layer node IDs (layer1, layer2, layer3, gw)
|
||||
- Success flag (boolean)
|
||||
- Timestamp
|
||||
|
||||
2. To analyze routes:
|
||||
- Check `NetworkAccount` and `AccountingRoute` in `nym-network-monitor/src/accounting.rs`
|
||||
- View monitoring logic in `common/nymsphinx/chunking/monitoring.rs`
|
||||
- Observe how routes are submitted to the database in the `submit_accounting_routes_to_db` function
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Profiling and Benchmarking
|
||||
|
||||
```bash
|
||||
# Run benchmarks
|
||||
cargo bench -p nym-node
|
||||
|
||||
# Profile with perf (Linux)
|
||||
cargo build --release --features profiling
|
||||
perf record --call-graph=dwarf ./target/release/nym-node run --mode mixnode
|
||||
perf report
|
||||
|
||||
# Generate flamegraph
|
||||
cargo install flamegraph
|
||||
cargo flamegraph --bin nym-node -- run --mode mixnode
|
||||
```
|
||||
|
||||
### Common Performance Considerations
|
||||
|
||||
- Use bounded channels for backpressure
|
||||
- Batch database operations where possible
|
||||
- Monitor memory usage with `RUST_LOG=nym_node::metrics=debug`
|
||||
- Use connection pooling for database connections
|
||||
- Consider using `jemalloc` for better memory allocation performance
|
||||
Generated
+585
-1594
File diff suppressed because it is too large
Load Diff
+45
-38
@@ -72,10 +72,6 @@ members = [
|
||||
"common/nym-cache",
|
||||
"common/nym-connection-monitor",
|
||||
"common/nym-id",
|
||||
"common/nym-kcp",
|
||||
"common/nym-lp",
|
||||
"common/nym-lp-common",
|
||||
"common/nym-kkt",
|
||||
"common/nym-metrics",
|
||||
"common/nym_offline_compact_ecash",
|
||||
"common/nymnoise",
|
||||
@@ -157,14 +153,13 @@ members = [
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/mixnet-connectivity-check",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/validator-status-check",
|
||||
"tools/nym-cli",
|
||||
"tools/nym-id-cli",
|
||||
"tools/nym-lp-client",
|
||||
"tools/nym-nr-query",
|
||||
"tools/nymvisor",
|
||||
"tools/ts-rs-cli",
|
||||
@@ -172,10 +167,20 @@ members = [
|
||||
# "wasm/full-nym-wasm", # If we uncomment this again, remember to also uncomment the profile settings below
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/libp2p-nym",
|
||||
"wasm/zknym-lib",
|
||||
"nym-gateway-probe",
|
||||
"integration-tests", "common/nym-lp-transport", "common/nym-kkt-ciphersuite",
|
||||
|
||||
# experimenting with moving contracts/ workspace members into here
|
||||
"contracts/coconut-dkg",
|
||||
"contracts/ecash",
|
||||
"contracts/mixnet",
|
||||
"contracts/mixnet-vesting-integration-tests",
|
||||
"contracts/nym-pool",
|
||||
"contracts/multisig/cw3-flex-multisig",
|
||||
"contracts/multisig/cw4-group",
|
||||
"contracts/vesting",
|
||||
"contracts/performance",
|
||||
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -186,16 +191,16 @@ default-members = [
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-node",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-node-status-api/nym-node-status-api",
|
||||
"nym-statistics-api",
|
||||
"nym-validator-rewarder",
|
||||
"nyx-chain-watcher",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"tools/nymvisor",
|
||||
"nym-registration-client"
|
||||
]
|
||||
|
||||
exclude = ["contracts", "nym-wallet", "cpu-cycles"]
|
||||
exclude = ["nym-wallet", "cpu-cycles"]
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Nym Technologies SA"]
|
||||
@@ -215,7 +220,6 @@ aes = "0.8.1"
|
||||
aes-gcm = "0.10.1"
|
||||
aes-gcm-siv = "0.11.1"
|
||||
ammonia = "4"
|
||||
ansi_term = "0.12"
|
||||
anyhow = "1.0.98"
|
||||
arc-swap = "1.7.1"
|
||||
argon2 = "0.5.0"
|
||||
@@ -255,9 +259,9 @@ criterion = "0.5"
|
||||
csv = "1.3.1"
|
||||
ctr = "0.9.1"
|
||||
cupid = "0.6.1"
|
||||
curve25519-dalek = "4.1.3"
|
||||
dashmap = "5.5.3"
|
||||
defguard_wireguard_rs = "0.8.0"
|
||||
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
|
||||
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
|
||||
digest = "0.10.7"
|
||||
dirs = "6.0"
|
||||
dotenvy = "0.15.6"
|
||||
@@ -295,9 +299,7 @@ inventory = "0.3.21"
|
||||
ip_network = "0.4.1"
|
||||
ipnetwork = "0.20"
|
||||
itertools = "0.14.0"
|
||||
jwt-simple = { version = "0.12.12", default-features = false, features = [
|
||||
"pure-rust",
|
||||
] }
|
||||
jwt-simple = { version = "0.12.12", default-features = false, features = ["pure-rust"] }
|
||||
k256 = "0.13"
|
||||
lazy_static = "1.5.0"
|
||||
ledger-transport = "0.10.0"
|
||||
@@ -305,9 +307,8 @@ ledger-transport-hid = "0.10.0"
|
||||
log = "0.4"
|
||||
mime = "0.3.17"
|
||||
moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.30.1"
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
num_enum = "0.7.5"
|
||||
once_cell = "1.21.3"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
@@ -338,7 +339,7 @@ serde_repr = "0.1"
|
||||
serde_with = "3.9.0"
|
||||
serde_yaml = "0.9.25"
|
||||
serde_plain = "1.0.2"
|
||||
sha2 = "0.10.3"
|
||||
sha2 = "0.10.9"
|
||||
si-scale = "0.2.3"
|
||||
snow = "0.9.6"
|
||||
sphinx-packet = "=0.6.0"
|
||||
@@ -354,7 +355,6 @@ test-with = { version = "0.15.4", default-features = false }
|
||||
tempfile = "3.20"
|
||||
thiserror = "2.0"
|
||||
time = "0.3.41"
|
||||
tls_codec = "0.4.1"
|
||||
tokio = "1.47"
|
||||
tokio-postgres = "0.7"
|
||||
tokio-stream = "0.1.17"
|
||||
@@ -402,7 +402,6 @@ nym-client-core = { version = "1.20.1", path = "common/client-core", default-fea
|
||||
nym-client-core-config-types = { version = "1.20.1", path = "common/client-core/config-types" }
|
||||
nym-client-core-gateways-storage = { version = "1.20.1", path = "common/client-core/gateways-storage" }
|
||||
nym-client-core-surb-storage = { version = "1.20.1", path = "common/client-core/surb-storage" }
|
||||
nym-client-websocket-requests = { version = "1.20.1", path = "clients/native/websocket-requests" }
|
||||
nym-common = { version = "1.20.1", path = "common/nym-common" }
|
||||
nym-compact-ecash = { version = "1.20.1", path = "common/nym_offline_compact_ecash" }
|
||||
nym-config = { version = "1.20.1", path = "common/config" }
|
||||
@@ -410,13 +409,11 @@ nym-contracts-common = { version = "1.20.1", path = "common/cosmwasm-smart-contr
|
||||
nym-coconut-dkg-common = { version = "1.20.1", path = "common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nym-credential-storage = { version = "1.20.1", path = "common/credential-storage" }
|
||||
nym-credential-utils = { version = "1.20.1", path = "common/credential-utils" }
|
||||
nym-credential-proxy-lib = { version = "1.20.1", path = "common/credential-proxy" }
|
||||
nym-credentials = { version = "1.20.1", path = "common/credentials", default-features = false }
|
||||
nym-credentials-interface = { version = "1.20.1", path = "common/credentials-interface" }
|
||||
nym-credential-proxy-requests = { version = "1.20.1", path = "nym-credential-proxy/nym-credential-proxy-requests", default-features = false }
|
||||
nym-credential-verification = { version = "1.20.1", path = "common/credential-verification" }
|
||||
nym-crypto = { version = "1.20.1", path = "common/crypto", default-features = false }
|
||||
nym-dkg = { version = "1.20.1", path = "common/dkg" }
|
||||
nym-ecash-contract-common = { version = "1.20.1", path = "common/cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-signer-check = { version = "1.20.1", path = "common/ecash-signer-check" }
|
||||
nym-ecash-signer-check-types = { version = "1.20.1", path = "common/ecash-signer-check-types" }
|
||||
@@ -426,13 +423,11 @@ nym-ffi-shared = { version = "1.20.1", path = "sdk/ffi/shared" }
|
||||
nym-gateway-client = { version = "1.20.1", path = "common/client-libs/gateway-client", default-features = false }
|
||||
nym-gateway-requests = { version = "1.20.1", path = "common/gateway-requests" }
|
||||
nym-gateway-storage = { version = "1.20.1", path = "common/gateway-storage" }
|
||||
nym-gateway-stats-storage = { version = "1.20.1", path = "common/gateway-stats-storage" }
|
||||
nym-group-contract-common = { version = "1.20.1", path = "common/cosmwasm-smart-contracts/group-contract" }
|
||||
nym-http-api-client = { version = "1.20.1", path = "common/http-api-client" }
|
||||
nym-http-api-client-macro = { version = "1.20.1", path = "common/http-api-client-macro" }
|
||||
nym-http-api-common = { version = "1.20.1", path = "common/http-api-common", default-features = false }
|
||||
nym-id = { version = "1.20.1", path = "common/nym-id" }
|
||||
nym-kkt-ciphersuite = { path = "common/nym-kkt-ciphersuite" }
|
||||
nym-ip-packet-client = { version = "1.20.1", path = "nym-ip-packet-client" }
|
||||
nym-ip-packet-requests = { version = "1.20.1", path = "common/ip-packet-requests" }
|
||||
nym-metrics = { version = "1.20.1", path = "common/nym-metrics" }
|
||||
@@ -480,23 +475,32 @@ nym-types = { version = "1.20.1", path = "common/types" }
|
||||
nym-upgrade-mode-check = { version = "1.20.1", path = "common/upgrade-mode-check" }
|
||||
nym-validator-client = { version = "1.20.1", path = "common/client-libs/validator-client", default-features = false }
|
||||
nym-vesting-contract-common = { version = "1.20.1", path = "common/cosmwasm-smart-contracts/vesting-contract" }
|
||||
nym-verloc = { version = "1.20.1", path = "common/verloc" }
|
||||
nym-wireguard = { version = "1.20.1", path = "common/wireguard" }
|
||||
nym-wireguard-types = { version = "1.20.1", path = "common/wireguard-types" }
|
||||
nym-wireguard-private-metadata-shared = { version = "1.20.1", path = "common/wireguard-private-metadata/shared" }
|
||||
nym-wireguard-private-metadata-client = { version = "1.20.1", path = "common/wireguard-private-metadata/client" }
|
||||
nym-wireguard-private-metadata-server = { version = "1.20.1", path = "common/wireguard-private-metadata/server" }
|
||||
nym-wireguard-private-metadata-shared = { version = "1.20.1", path = "common/wireguard-private-metadata/shared" }
|
||||
nym-wireguard-private-metadata-client = { version = "1.20.1", path = "common/wireguard-private-metadata/client" }
|
||||
nym-wireguard-private-metadata-server = { version = "1.20.1", path = "common/wireguard-private-metadata/server" }
|
||||
nym-sqlx-pool-guard = { version = "1.2.0", path = "nym-sqlx-pool-guard" }
|
||||
nym-wasm-client-core = { version = "1.20.1", path = "common/wasm/client-core" }
|
||||
nym-wasm-storage = { version = "1.20.1", path = "common/wasm/storage" }
|
||||
nym-wasm-utils = { version = "1.20.1", path = "common/wasm/utils", default-features = false }
|
||||
nyxd-scraper-shared = { version = "1.20.1", path = "common/nyxd-scraper-shared" }
|
||||
|
||||
# experimenting with moving contracts/ workspace members into here
|
||||
cw3-flex-multisig = { version = "2.0.0", path = "contracts/multisig/cw3-flex-multisig" }
|
||||
cw4-group = { version = "2.0.0", path = "contracts/multisig/cw4-group" }
|
||||
|
||||
nym-contracts-common-testing = { version = "1.20.1", path = "common/cosmwasm-smart-contracts/contracts-common-testing" }
|
||||
easy-addr = { version = "1.20.1", path = "common/cosmwasm-smart-contracts/easy_addr" }
|
||||
nym-pool-contract-common = { version = "1.20.1", path = "common/cosmwasm-smart-contracts/nym-pool-contract" }
|
||||
nym-mixnet-contract = { version = "1.5.1", path = "contracts/mixnet" }
|
||||
|
||||
|
||||
# coconut/DKG related
|
||||
# unfortunately until https://github.com/zkcrypto/nym-bls12_381-fork/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
|
||||
# plus to make our live easier we need serde support from https://github.com/zkcrypto/nym-bls12_381-fork/pull/125
|
||||
nym-bls12_381-fork = { version = "0.8.0-forked", default-features = false }
|
||||
# plus to make our live easier we need serde support from https://github.com/zkcrypto/bls12_381/pull/125
|
||||
# MAX TODO upload this to crates.io and then pull in from there
|
||||
bls12_381 = { git = "https://github.com/nymtech/bls12_381", default-features = false, branch = "temp/experimental-serdect-updated" }
|
||||
group = { version = "0.13.0", default-features = false }
|
||||
ff = { version = "0.13.1", default-features = false }
|
||||
subtle = "2.5.0"
|
||||
@@ -513,6 +517,14 @@ cw4 = { version = "=2.0.0" }
|
||||
cw-controllers = { version = "=2.0.0" }
|
||||
cw-multi-test = "=2.3.2"
|
||||
|
||||
# experimenting with moving contracts/ workspace members into here - additional required deps
|
||||
cosmwasm-crypto = "=2.2.2"
|
||||
cosmwasm-derive = "=2.2.2"
|
||||
cw3-fixed-multisig = "=2.0.0"
|
||||
cw20 = "=2.0.0"
|
||||
cw20-base = "2.0.0"
|
||||
sylvia = "1.3.3"
|
||||
|
||||
# cosmrs-related
|
||||
bip32 = { version = "0.5.3", default-features = false }
|
||||
|
||||
@@ -544,11 +556,6 @@ web-sys = "0.3.76"
|
||||
#[patch.crates-io]
|
||||
#sphinx-packet = { path = "../sphinx" }
|
||||
|
||||
# Patch multiaddr with Protocol::Nym support for libp2p-nym transport
|
||||
[patch.crates-io.multiaddr]
|
||||
git = "https://github.com/mfahampshire/rust-multiaddr"
|
||||
branch = "nym-protocol"
|
||||
|
||||
# Profile settings for individual crates
|
||||
|
||||
# Compile-time verified queries do quite a bit of work at compile time. Incremental
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
ansible_ssh_private_key_file: ~/.ssh/<SSH_KEY>
|
||||
|
||||
# nym_version: "v2025.21-mozzarella"
|
||||
#
|
||||
#
|
||||
# NOTE:
|
||||
# if you want to pin Nym to a specific version instead of using the
|
||||
# latest release from GitHub in /tasks/main.yml then
|
||||
@@ -13,17 +13,17 @@ tunnel_manager_url: "https://github.com/nymtech/nym/raw/refs/heads/develop/scrip
|
||||
quic_bridge_deployment_url: "https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/quic_bridge_deployment.sh"
|
||||
|
||||
# NOTE: These values will be used globally unless overwritten per node in inventory/all
|
||||
ansible_user: root # used for ssh, like `ssh root@nym-exit.ch-1.mynodes.net`
|
||||
email: "<EMAIL>" # used in certbot, description.toml and landing page
|
||||
website: "<WEBSITE>" # it is used in the description.toml
|
||||
description: "<NODE_PUBLIC_DESCRIPTION>" # or define per node in inventory/all
|
||||
ansible_user: root # used for ssh, like `ssh root@nym-exit.ch-1.mynodes.net`
|
||||
email: "<EMAIL>" # used in certbot, description.toml and landing page
|
||||
website: "<WEBSITE>" # it is used in the description.toml
|
||||
description: "<NODE_PUBLIC_DESCRIPTION>" # or define per node in inventory/all
|
||||
|
||||
# NOTE: Set these vars if you want them globally for all nodes
|
||||
# Per node changes in inventory/all will overwrite these global ones:
|
||||
hostname: "" # this is a fallback, keep it and setup hostname per node in inventory/all
|
||||
# moniker: "<MONIKER>" # if not setup here not in inventory/all it get's derived from the hostname
|
||||
# mode: <MODE> # entry-gateway/exit-gateway/mixnode
|
||||
# wireguard_enabled: <WIREGUARD_ENABLED> # true/false
|
||||
hostname: "" # this is a fallback, keep it and setup hostname per node in inventory/all
|
||||
# moniker: "<MONIKER>" # if not setup here not in inventory/all it get's derived from the hostname
|
||||
# mode: <MODE> # entry-gateway/exit-gateway/mixnode
|
||||
# wireguard_enabled: <WIREGUARD_ENABLED> # true/false
|
||||
|
||||
# NOTE: Possible vars to incule on landing page, etc.
|
||||
# operator_name: "<OPERATOR_NAME>"
|
||||
@@ -41,4 +41,4 @@ packages:
|
||||
- ca-certificates
|
||||
- jq
|
||||
- wget
|
||||
- ufw
|
||||
- ufw
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
- name: Set hostname
|
||||
hostname:
|
||||
name: "{{ hostname }}"
|
||||
when: hostname is defined and hostname | length > 0
|
||||
|
||||
- name: Install aptitude
|
||||
- name: Install aptitude
|
||||
apt:
|
||||
name: aptitude
|
||||
update_cache: yes
|
||||
@@ -15,9 +14,9 @@
|
||||
apt:
|
||||
update_cache: yes
|
||||
upgrade: yes
|
||||
|
||||
|
||||
- name: Install essential packages
|
||||
package:
|
||||
name: "{{ packages }}"
|
||||
state: latest
|
||||
update_cache: yes
|
||||
update_cache: yes
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
- name: Reload nginx
|
||||
service:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
||||
- name: Restart nginx
|
||||
service:
|
||||
name: nginx
|
||||
state: restarted
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
- name: Install nginx and certbot
|
||||
apt:
|
||||
name:
|
||||
@@ -6,168 +5,57 @@
|
||||
- certbot
|
||||
- python3-certbot-nginx
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: Ensure nginx snippets directory exists
|
||||
file:
|
||||
path: /etc/nginx/snippets
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
# own SSL defaults - don't rely on certbot files
|
||||
- name: Install Nym SSL options snippet
|
||||
copy:
|
||||
dest: /etc/nginx/snippets/nym-ssl-options.conf
|
||||
mode: "0644"
|
||||
content: |
|
||||
ssl_session_cache shared:NYMSSL:10m;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# Reasonable modern cipher set (works across Ubuntu nginx builds)
|
||||
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305";
|
||||
|
||||
# OCSP stapling is nice but can break if resolver isn't set; keep minimal here.
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Ensure web root directory exists
|
||||
- name: Create web root directory
|
||||
file:
|
||||
path: "/var/www/{{ hostname }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Deploy landing page
|
||||
- name: Create landing page template
|
||||
tags: landing
|
||||
template:
|
||||
src: landing.html.j2
|
||||
dest: "/var/www/{{ hostname }}/index.html"
|
||||
mode: "0644"
|
||||
notify: Restart nginx
|
||||
|
||||
# remove default site - safe on fresh + redeploy
|
||||
- name: Disable default nginx site symlink
|
||||
- name: Remove default nginx site
|
||||
file:
|
||||
path: /etc/nginx/sites-enabled/default
|
||||
state: absent
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Remove default nginx site definition if present
|
||||
file:
|
||||
path: /etc/nginx/sites-available/default
|
||||
state: absent
|
||||
notify: Restart nginx
|
||||
|
||||
# always deploy/enable HTTP vhost
|
||||
- name: Deploy HTTP vhost
|
||||
- name: Add bare-bones nginx template
|
||||
template:
|
||||
src: nginx-site.conf.j2
|
||||
dest: "/etc/nginx/sites-available/{{ hostname }}"
|
||||
mode: "0644"
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Enable HTTP vhost (force correct symlink)
|
||||
- name: Enable nginx config
|
||||
file:
|
||||
src: "/etc/nginx/sites-available/{{ hostname }}"
|
||||
dest: "/etc/nginx/sites-enabled/{{ hostname }}"
|
||||
state: link
|
||||
force: true
|
||||
notify: Restart nginx
|
||||
|
||||
# detect if cert exists already
|
||||
- name: Check whether certificate exists
|
||||
stat:
|
||||
path: "/etc/letsencrypt/live/{{ hostname }}/fullchain.pem"
|
||||
register: le_cert
|
||||
|
||||
# if cert does NOT exist yet, ensure SSL/WSS are NOT enabled
|
||||
- name: Ensure SSL and WSS vhosts are disabled until cert exists
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "/etc/nginx/sites-enabled/{{ hostname }}-ssl"
|
||||
- "/etc/nginx/sites-enabled/nym-wss-config"
|
||||
when: not le_cert.stat.exists
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Ensure nginx is enabled and running (needed for ACME http-01)
|
||||
service:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: Validate nginx configuration (HTTP stage)
|
||||
- name: Validate nginx configuration
|
||||
command: nginx -t
|
||||
changed_when: false
|
||||
|
||||
- name: Flush handlers (ensure HTTP is active before certbot)
|
||||
meta: flush_handlers
|
||||
|
||||
# certbot strategy:
|
||||
# - if cert exists: webroot - doesn't touch nginx
|
||||
# - else: --nginx works first-time; may touch nginx
|
||||
- name: Obtain/renew certificate
|
||||
- name: Obtain SSL certificate
|
||||
command:
|
||||
cmd: >-
|
||||
{% if le_cert.stat.exists %}
|
||||
certbot certonly --webroot
|
||||
-w /var/www/{{ hostname }}
|
||||
--non-interactive --agree-tos --keep-until-expiring
|
||||
-m {{ email }} -d {{ hostname }}
|
||||
{% else %}
|
||||
certbot --nginx
|
||||
--non-interactive --agree-tos --redirect
|
||||
-m {{ email }} -d {{ hostname }}
|
||||
{% endif %}
|
||||
register: certbot_result
|
||||
failed_when: false
|
||||
cmd: "certbot --nginx --non-interactive --agree-tos --redirect -m {{ email }} -d {{ hostname }}"
|
||||
|
||||
# re-check cert after certbot attempt
|
||||
- name: Re-check whether certificate exists after certbot
|
||||
stat:
|
||||
path: "/etc/letsencrypt/live/{{ hostname }}/fullchain.pem"
|
||||
register: le_cert_after
|
||||
|
||||
# only deploy/enable SSL & WSS if cert exists
|
||||
- name: Deploy HTTPS vhost for {{ hostname }}
|
||||
template:
|
||||
src: nginx-site-ssl.conf.j2
|
||||
dest: "/etc/nginx/sites-available/{{ hostname }}-ssl"
|
||||
mode: "0644"
|
||||
when: le_cert_after.stat.exists
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Enable HTTPS vhost (force correct symlink)
|
||||
file:
|
||||
src: "/etc/nginx/sites-available/{{ hostname }}-ssl"
|
||||
dest: "/etc/nginx/sites-enabled/{{ hostname }}-ssl"
|
||||
state: link
|
||||
force: true
|
||||
when: le_cert_after.stat.exists
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Deploy WSS vhost
|
||||
- name: Add wss config from nginx template
|
||||
template:
|
||||
src: wss-config.conf.j2
|
||||
dest: "/etc/nginx/sites-available/nym-wss-config"
|
||||
mode: "0644"
|
||||
when: le_cert_after.stat.exists
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Enable WSS vhost (force correct symlink)
|
||||
- name: Enable WSS config
|
||||
file:
|
||||
src: "/etc/nginx/sites-available/nym-wss-config"
|
||||
dest: "/etc/nginx/sites-enabled/nym-wss-config"
|
||||
state: link
|
||||
force: true
|
||||
when: le_cert_after.stat.exists
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Validate nginx configuration (final)
|
||||
- name: Validate nginx config after wss
|
||||
command: nginx -t
|
||||
changed_when: false
|
||||
|
||||
- name: Flush handlers (apply restart after successful tests)
|
||||
meta: flush_handlers
|
||||
- name: Restart nginx to apply changes
|
||||
service: name=nginx state=restarted enabled=yes
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
||||
server_name {{ hostname }};
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem;
|
||||
include /etc/nginx/snippets/nym-ssl-options.conf;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,10 @@ server {
|
||||
|
||||
server_name {{ hostname }};
|
||||
|
||||
root /var/www/{{ hostname }};
|
||||
index index.html;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
default_type "text/plain";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,10 @@ server {
|
||||
|
||||
server_name {{ hostname }};
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem;
|
||||
include /etc/nginx/snippets/nym-ssl-options.conf;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
@@ -6,6 +6,10 @@ nym_install_dir: /root/nym-binaries
|
||||
http_bind_address: "0.0.0.0:8080" # maps to --http-bind-address
|
||||
mixnet_bind_address: "0.0.0.0:1789" # maps to --mixnet-bind-address
|
||||
|
||||
|
||||
# WireGuard boolean
|
||||
wireguard_enabled: "{{ wireguard_enabled | default(false) | bool }}"
|
||||
|
||||
# Landing page base dir, hostname is appended in the task
|
||||
landing_page_assets_base_dir: "/var/www"
|
||||
|
||||
@@ -33,4 +37,4 @@ nym_ufw_rules:
|
||||
- { port: 8080, proto: tcp }
|
||||
- { port: 9000, proto: tcp }
|
||||
- { port: 9001, proto: tcp }
|
||||
- { port: 51822, proto: udp }
|
||||
- { port: 51822, proto: udp }
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
- name: Reload systemd
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
# useful when the host is behind a NAT
|
||||
# Useful when the host is behind a NAT
|
||||
- name: Fetch the public IP address
|
||||
command: "curl -4 canhazip.com"
|
||||
register: ipv4
|
||||
@@ -11,7 +11,7 @@
|
||||
public_ip: "{{ ipv4.stdout | default(ansible_default_ipv4.address) }}"
|
||||
|
||||
- name: Initialize nym node
|
||||
# delete the part from --hostname onward if you run mode=mixnode only
|
||||
# Delete the part from --hostname onward if you run mode=mixnode only
|
||||
command:
|
||||
cmd: >
|
||||
{{ nym_install_dir }}/nym-node run
|
||||
@@ -25,7 +25,7 @@
|
||||
{{ nym_extra_flags }}
|
||||
|
||||
--hostname {{ hostname }}
|
||||
--wireguard-enabled {{ (wireguard_enabled | default('false') | bool) | ternary('true','false') }}
|
||||
--wireguard-enabled {{ wireguard_enabled }}
|
||||
--landing-page-assets-path {{ landing_page_assets_base_dir }}/{{ hostname }}/
|
||||
{% if nym_write_flag %}-w{% endif %}
|
||||
{% if nym_init_only_flag %}--init-only{% endif %}
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
- name: Ensure UFW is installed
|
||||
apt:
|
||||
name: ufw
|
||||
state: present
|
||||
update_cache: yes
|
||||
when: nym_ufw_enable
|
||||
|
||||
|
||||
- name: Configure UFW rules
|
||||
ufw:
|
||||
rule: allow
|
||||
@@ -23,10 +14,9 @@
|
||||
- name: Allow bandwidth/topup rule inside WG tunnel
|
||||
command: >
|
||||
ufw allow in on nymwg to any port 51830 proto tcp comment 'bandwidth queries/topup'
|
||||
changed_when: false
|
||||
when:
|
||||
- nym_ufw_enable
|
||||
- (wireguard_enabled | default(false) | bool)
|
||||
- (wireguard_enabled | bool)
|
||||
|
||||
- name: Enable UFW
|
||||
ufw:
|
||||
|
||||
@@ -6,10 +6,10 @@ StartLimitBurst=10
|
||||
[Service]
|
||||
User={{ ansible_user }}
|
||||
LimitNOFILE=65536
|
||||
ExecStart=/root/nym-binaries/nym-node run --mode {{ mode }} --accept-operator-terms-and-conditions --wireguard-enabled {{ (wireguard_enabled | default(false) | bool) | ternary('true','false') }}
|
||||
ExecStart=/root/nym-binaries/nym-node run --mode {{ mode }} --accept-operator-terms-and-conditions --wireguard-enabled {{ wireguard_enabled }}
|
||||
KillSignal=SIGINT
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,11 +1,14 @@
|
||||
---
|
||||
- name: Download network-tunnel-manager.sh
|
||||
tags: network tunnel manager
|
||||
get_url:
|
||||
url: "{{ tunnel_manager_url }}"
|
||||
dest: "/root/nym-binaries/network-tunnel-manager.sh"
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure tunnel manager
|
||||
tags:
|
||||
- network_tunnel_manager
|
||||
tags: network tunnel manager
|
||||
become: true
|
||||
command:
|
||||
cmd: "/root/nym-binaries/network-tunnel-manager.sh {{ item }}"
|
||||
loop:
|
||||
- complete_networking_configuration
|
||||
register: tunnel_mgr
|
||||
failed_when: false
|
||||
- complete_networking_configuration
|
||||
@@ -9,7 +9,7 @@
|
||||
changed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
# show the full stdout
|
||||
# show the full stdout so we don’t depend on regex parsing at all
|
||||
# show full upgraded version output, line by line
|
||||
- name: Show upgraded nym-node version info
|
||||
debug:
|
||||
@@ -116,7 +116,7 @@
|
||||
when: not ansible_check_mode and (upgrade_ok | default(false)) == false
|
||||
|
||||
# optional: hard-fail the play for CI environments
|
||||
#- name: fail the play to signal upgrade failure
|
||||
#- name: Fail the play to signal upgrade failure
|
||||
# fail:
|
||||
# msg: "nym-node upgrade failed; rolled back to previous binary."
|
||||
# when: not ansible_check_mode and (upgrade_ok | default(false)) == false
|
||||
|
||||
+14
-14
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.69"
|
||||
version = "1.1.67"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
@@ -44,31 +44,31 @@ zeroize = { workspace = true }
|
||||
|
||||
## internal
|
||||
nym-bandwidth-controller = { workspace = true }
|
||||
nym-bin-common = { workspace = true, features = [
|
||||
nym-bin-common = { path = "../../common/bin-common", features = [
|
||||
"output_format",
|
||||
"clap",
|
||||
"basic_tracing",
|
||||
] }
|
||||
nym-client-core = { workspace = true, features = [
|
||||
nym-client-core = { path = "../../common/client-core", features = [
|
||||
"fs-credentials-storage",
|
||||
"fs-surb-storage",
|
||||
"fs-gateways-storage",
|
||||
"cli",
|
||||
] }
|
||||
nym-config = { workspace = true }
|
||||
nym-credential-storage = { workspace = true }
|
||||
nym-credentials = { workspace = true }
|
||||
nym-crypto = { workspace = true }
|
||||
nym-gateway-requests = { workspace = true }
|
||||
nym-network-defaults = { workspace = true }
|
||||
nym-sphinx = { workspace = true }
|
||||
nym-pemstore = { workspace = true }
|
||||
nym-task = { workspace = true }
|
||||
nym-topology = { workspace = true }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-gateway-requests = { path = "../../common/gateway-requests" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-pemstore = { path = "../../common/pemstore" }
|
||||
nym-task = { path = "../../common/task" }
|
||||
nym-topology = { path = "../../common/topology" }
|
||||
nym-validator-client = { workspace = true, features = [
|
||||
"http-client",
|
||||
] }
|
||||
nym-client-websocket-requests = { workspace = true }
|
||||
nym-id = { workspace = true }
|
||||
nym-client-websocket-requests = { path = "websocket-requests" }
|
||||
nym-id = { path = "../../common/nym-id" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.69"
|
||||
version = "1.1.67"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
@@ -25,29 +25,29 @@ url = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-bin-common = { workspace = true, features = [
|
||||
nym-bin-common = { path = "../../common/bin-common", features = [
|
||||
"output_format",
|
||||
"clap",
|
||||
"basic_tracing",
|
||||
] }
|
||||
nym-client-core = { workspace = true, features = [
|
||||
nym-client-core = { path = "../../common/client-core", features = [
|
||||
"fs-credentials-storage",
|
||||
"fs-surb-storage",
|
||||
"fs-gateways-storage",
|
||||
"cli",
|
||||
] }
|
||||
nym-config = { workspace = true }
|
||||
nym-credential-storage = { workspace = true }
|
||||
nym-credentials = { workspace = true }
|
||||
nym-crypto = { workspace = true }
|
||||
nym-gateway-requests = { workspace = true }
|
||||
nym-id = { workspace = true }
|
||||
nym-network-defaults = { workspace = true }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-gateway-requests = { path = "../../common/gateway-requests" }
|
||||
nym-id = { path = "../../common/nym-id" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-ordered-buffer = { workspace = true }
|
||||
nym-pemstore = { workspace = true }
|
||||
nym-socks5-client-core = { workspace = true }
|
||||
nym-sphinx = { workspace = true }
|
||||
nym-topology = { workspace = true }
|
||||
nym-pemstore = { path = "../../common/pemstore" }
|
||||
nym-socks5-client-core = { path = "../../common/socks5-client-core" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-topology = { path = "../../common/topology" }
|
||||
nym-validator-client = { workspace = true, features = [
|
||||
"http-client",
|
||||
] }
|
||||
|
||||
@@ -20,12 +20,13 @@ use nym_crypto::asymmetric::x25519::{PrivateKey, PublicKey};
|
||||
use sha2::Sha256;
|
||||
|
||||
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
|
||||
pub type PrivateIPs = HashMap<IpPair, SystemTime>;
|
||||
pub type PrivateIPs = HashMap<IpPair, Taken>;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct IpPair {
|
||||
|
||||
@@ -28,7 +28,6 @@ pub use traits::{BandwidthTicketProvider, DEFAULT_TICKETS_TO_SPEND};
|
||||
pub mod acquire;
|
||||
pub mod error;
|
||||
mod event;
|
||||
pub mod mock;
|
||||
mod traits;
|
||||
mod utils;
|
||||
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(clippy::expect_used)]
|
||||
|
||||
use crate::error::BandwidthControllerError;
|
||||
use crate::{BandwidthTicketProvider, PreparedCredential, PreparedCredentialMetadata};
|
||||
use async_trait::async_trait;
|
||||
use nym_credentials_interface::{CredentialSpendingData, TicketType};
|
||||
use nym_crypto::asymmetric::ed25519::PublicKey;
|
||||
use nym_ecash_time::OffsetDateTime;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MockBandwidthController {
|
||||
// TODO: inject proper bls381 keys and just sign credentials
|
||||
//
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl BandwidthTicketProvider for MockBandwidthController {
|
||||
async fn get_ecash_ticket(
|
||||
&self,
|
||||
ticket_type: TicketType,
|
||||
_gateway_id: PublicKey,
|
||||
tickets_to_spend: u32,
|
||||
) -> Result<PreparedCredential, BandwidthControllerError> {
|
||||
assert_eq!(tickets_to_spend, 1);
|
||||
|
||||
// This is a valid serialized CredentialSpendingData taken from integration tests
|
||||
// See: common/wireguard-private-metadata/tests/src/lib.rs:CREDENTIAL_BYTES
|
||||
const CREDENTIAL_BYTES: [u8; 1245] = [
|
||||
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227,
|
||||
254, 16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91,
|
||||
11, 139, 154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118,
|
||||
119, 249, 123, 35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125,
|
||||
135, 140, 99, 1, 151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255,
|
||||
166, 167, 186, 43, 90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160,
|
||||
16, 130, 59, 76, 193, 39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7,
|
||||
164, 240, 164, 205, 37, 81, 184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123,
|
||||
203, 57, 233, 243, 215, 145, 195, 196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219,
|
||||
180, 40, 58, 116, 92, 254, 160, 98, 48, 92, 254, 232, 107, 184, 80, 234, 60, 160, 235,
|
||||
249, 76, 41, 38, 165, 28, 40, 136, 74, 48, 166, 50, 245, 23, 201, 140, 101, 79, 93,
|
||||
235, 128, 186, 146, 126, 180, 134, 43, 13, 186, 19, 195, 48, 168, 201, 29, 216, 95,
|
||||
176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38, 228, 199, 122, 226, 217, 75, 40,
|
||||
191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85, 88, 199, 162, 17, 96, 103, 83,
|
||||
178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241, 85, 199, 11, 198, 226, 234,
|
||||
70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112, 48, 185, 22, 159, 67, 109,
|
||||
49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31, 97, 134, 204, 145, 27,
|
||||
181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203, 71, 121, 169, 126,
|
||||
151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107, 213, 90, 2,
|
||||
203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180, 178, 204,
|
||||
206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1, 96,
|
||||
151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
|
||||
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221,
|
||||
212, 207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127,
|
||||
240, 71, 223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192,
|
||||
213, 22, 119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72,
|
||||
243, 1, 48, 131, 59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30,
|
||||
202, 151, 28, 119, 240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166,
|
||||
67, 142, 121, 207, 202, 58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209,
|
||||
36, 194, 27, 63, 233, 144, 189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29,
|
||||
240, 13, 81, 142, 140, 133, 33, 30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201,
|
||||
165, 51, 252, 9, 28, 209, 1, 48, 150, 74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217,
|
||||
171, 184, 42, 148, 53, 150, 57, 50, 6, 227, 227, 62, 49, 42, 148, 148, 157, 82, 191,
|
||||
58, 24, 34, 56, 98, 120, 89, 105, 176, 85, 15, 253, 241, 41, 153, 195, 136, 1, 48, 142,
|
||||
126, 213, 101, 223, 79, 133, 230, 105, 38, 161, 149, 2, 21, 136, 150, 42, 72, 218, 85,
|
||||
146, 63, 223, 58, 108, 186, 183, 248, 62, 20, 47, 34, 113, 160, 177, 204, 181, 16, 24,
|
||||
212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48, 135, 242, 62, 149, 230, 178, 32, 224,
|
||||
119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170, 150, 96, 125, 136, 221, 180, 78,
|
||||
18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109, 55, 183, 40, 131, 172, 161, 88,
|
||||
183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249, 87, 159, 115, 61, 91, 55,
|
||||
130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144, 178, 55, 249, 64,
|
||||
211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32, 203, 116, 63,
|
||||
7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184, 96, 47,
|
||||
194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169, 24,
|
||||
212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
|
||||
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170,
|
||||
82, 108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97,
|
||||
0, 1, 32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234,
|
||||
175, 234, 150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40,
|
||||
220, 82, 241, 203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43,
|
||||
214, 0, 135, 217, 160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163,
|
||||
253, 244, 13, 202, 52, 147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10,
|
||||
205, 238, 1, 22, 83, 81, 70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155,
|
||||
106, 130, 171, 226, 210, 233, 178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156,
|
||||
207, 208, 107, 1, 32, 102, 31, 4, 98, 110, 156, 144, 61, 229, 140, 198, 84, 196, 238,
|
||||
128, 35, 131, 182, 137, 125, 241, 95, 69, 131, 170, 27, 2, 144, 75, 72, 242, 102, 3,
|
||||
32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251, 197, 32, 169, 104, 123, 110, 90, 78,
|
||||
153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233, 162, 69, 32, 216, 169, 159, 116,
|
||||
95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106, 83, 235, 176, 41, 27, 248,
|
||||
48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145, 192, 94, 153, 69, 80, 128,
|
||||
241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124, 55, 11, 107, 142, 49,
|
||||
88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0, 0, 0, 0,
|
||||
0, 0, 1,
|
||||
];
|
||||
|
||||
let mut credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES)
|
||||
.expect("Failed to deserialize test credential - this is a bug in the test harness");
|
||||
|
||||
// change the ticket type to the requested ticket
|
||||
// note that verification outside mocks is going to fail
|
||||
credential.payment.t_type = ticket_type.to_repr() as u8;
|
||||
|
||||
// Update spend_date to today to pass validation
|
||||
credential.spend_date = OffsetDateTime::now_utc().date();
|
||||
|
||||
Ok(PreparedCredential {
|
||||
data: credential,
|
||||
epoch_id: 0,
|
||||
metadata: PreparedCredentialMetadata {
|
||||
ticketbook_id: 0,
|
||||
tickets_withdrawn: 1,
|
||||
used_tickets: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
@@ -57,22 +57,3 @@ where
|
||||
Ok(Some(token))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<T: BandwidthTicketProvider + ?Sized + Send> BandwidthTicketProvider for Box<T> {
|
||||
async fn get_ecash_ticket(
|
||||
&self,
|
||||
ticket_type: TicketType,
|
||||
gateway_id: ed25519::PublicKey,
|
||||
tickets_to_spend: u32,
|
||||
) -> Result<PreparedCredential, BandwidthControllerError> {
|
||||
(**self)
|
||||
.get_ecash_ticket(ticket_type, gateway_id, tickets_to_spend)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
|
||||
(**self).get_upgrade_mode_token().await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,10 +124,6 @@ impl BinaryBuildInformation {
|
||||
}
|
||||
}
|
||||
|
||||
// to whoever is thinking of modifying this struct.
|
||||
// you MUST NOT change its structure in any way - adding, removing or changing fields
|
||||
// otherwise, it will break old clients as bincode serialisation is not backwards compatible
|
||||
// even if you put `#[serde(default)]` all over the place
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
#[cfg_attr(feature = "bin_info_schema", derive(schemars::JsonSchema))]
|
||||
|
||||
@@ -14,7 +14,6 @@ documentation.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
clap = { workspace = true, optional = true }
|
||||
@@ -30,7 +29,6 @@ sha2 = { workspace = true }
|
||||
si-scale = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "macros"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
@@ -36,7 +36,6 @@ use crate::init::{
|
||||
types::{GatewaySetup, InitialisationResult},
|
||||
};
|
||||
use futures::channel::mpsc;
|
||||
use futures::SinkExt;
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core_config_types::{ForgetMe, RememberMe};
|
||||
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
|
||||
@@ -67,12 +66,12 @@ use std::os::raw::c_int as RawFd;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use tokio_util::sync::{PollSendError, PollSender};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(debug_assertions)]
|
||||
use nym_wasm_utils::console_log;
|
||||
use wasm_utils::console_log;
|
||||
|
||||
/// Default number of retries for Nym API requests when using network details with domain fronting.
|
||||
/// This allows the client to try alternative URLs if the primary endpoint is unavailable.
|
||||
@@ -113,7 +112,10 @@ pub struct ClientInput {
|
||||
}
|
||||
|
||||
impl ClientInput {
|
||||
pub async fn send(&mut self, message: InputMessage) -> Result<(), PollSendError<InputMessage>> {
|
||||
pub async fn send(
|
||||
&self,
|
||||
message: InputMessage,
|
||||
) -> Result<(), tokio::sync::mpsc::error::SendError<InputMessage>> {
|
||||
self.input_sender.send(message).await
|
||||
}
|
||||
}
|
||||
@@ -743,7 +745,7 @@ where
|
||||
config: &Config,
|
||||
user_agent: Option<UserAgent>,
|
||||
client_stats_id: String,
|
||||
input_sender: PollSender<InputMessage>,
|
||||
input_sender: Sender<InputMessage>,
|
||||
shutdown_tracker: &ShutdownTracker,
|
||||
) -> ClientStatsSender {
|
||||
tracing::info!("Starting statistics control...");
|
||||
@@ -1011,7 +1013,7 @@ where
|
||||
&self.config,
|
||||
self.user_agent.clone(),
|
||||
generate_client_stats_id(*self_address.identity()),
|
||||
tokio_util::sync::PollSender::new(input_sender.clone()),
|
||||
input_sender.clone(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
|
||||
@@ -1137,7 +1139,7 @@ where
|
||||
client_input: ClientInputStatus::AwaitingProducer {
|
||||
client_input: ClientInput {
|
||||
connection_command_sender: client_connection_tx,
|
||||
input_sender: PollSender::new(input_sender),
|
||||
input_sender,
|
||||
client_request_sender,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::{
|
||||
error::ClientCoreError,
|
||||
};
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-credentials-storage"))]
|
||||
pub 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::{GatewaysDetailsStore, InMemGatewaysDetails};
|
||||
|
||||
@@ -1,36 +1,22 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use crate::error::ClientCoreError;
|
||||
use crate::make_bincode_serializer;
|
||||
use bincode::Options;
|
||||
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_sphinx::receiver::ReconstructedMessage;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use tokio_util::{
|
||||
bytes::Buf,
|
||||
bytes::BytesMut,
|
||||
codec::{Decoder, Encoder},
|
||||
};
|
||||
|
||||
pub type InputMessageSender = tokio_util::sync::PollSender<InputMessage>;
|
||||
pub type InputMessageSender = tokio::sync::mpsc::Sender<InputMessage>;
|
||||
pub type InputMessageReceiver = tokio::sync::mpsc::Receiver<InputMessage>;
|
||||
|
||||
const LENGHT_ENCODING_PREFIX_SIZE: usize = 4;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub enum InputMessage {
|
||||
/// Fire an already prepared mix packets into the network.
|
||||
/// No guarantees are made about it. For example no retransmssion
|
||||
/// will be attempted if it gets dropped.
|
||||
///
|
||||
/// Packets are stored as pre-serialized bytes, which avoids
|
||||
/// requiring Serde on MixPacket.
|
||||
Premade {
|
||||
packet_bytes: Vec<Vec<u8>>,
|
||||
msgs: Vec<MixPacket>,
|
||||
lane: TransmissionLane,
|
||||
},
|
||||
|
||||
@@ -79,16 +65,12 @@ pub enum InputMessage {
|
||||
}
|
||||
|
||||
impl InputMessage {
|
||||
pub fn simple(data: &[u8], recipient: Recipient) -> Self {
|
||||
InputMessage::new_regular(recipient, data.to_vec(), TransmissionLane::General, None)
|
||||
}
|
||||
|
||||
pub fn new_premade(
|
||||
packet_bytes: Vec<Vec<u8>>,
|
||||
msgs: Vec<MixPacket>,
|
||||
lane: TransmissionLane,
|
||||
packet_type: PacketType,
|
||||
) -> Self {
|
||||
let message = InputMessage::Premade { packet_bytes, lane };
|
||||
let message = InputMessage::Premade { msgs, lane };
|
||||
if packet_type == PacketType::Mix {
|
||||
message
|
||||
} else {
|
||||
@@ -203,394 +185,4 @@ impl InputMessage {
|
||||
self.set_max_retransmissions(max_retransmissions);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
pub fn serialized_size(&self) -> u64 {
|
||||
make_bincode_serializer()
|
||||
.serialized_size(self)
|
||||
.expect("failed to get serialized InputMessage size")
|
||||
+ LENGHT_ENCODING_PREFIX_SIZE as u64
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AdressedInputMessageCodec(pub Recipient);
|
||||
|
||||
impl Encoder<&[u8]> for AdressedInputMessageCodec {
|
||||
type Error = ClientCoreError;
|
||||
|
||||
fn encode(&mut self, item: &[u8], buf: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
let mut codec = InputMessageCodec;
|
||||
let input_message = InputMessage::simple(item, self.0);
|
||||
codec.encode(input_message, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputMessageCodec;
|
||||
|
||||
impl Encoder<InputMessage> for InputMessageCodec {
|
||||
type Error = ClientCoreError;
|
||||
|
||||
fn encode(&mut self, item: InputMessage, buf: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
#[allow(clippy::expect_used)]
|
||||
let encoded = make_bincode_serializer().serialize(&item)?;
|
||||
let encoded_len = encoded.len() as u32;
|
||||
let mut encoded_with_len = encoded_len.to_le_bytes().to_vec();
|
||||
encoded_with_len.extend(encoded);
|
||||
buf.reserve(encoded_with_len.len());
|
||||
buf.extend_from_slice(&encoded_with_len);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for InputMessageCodec {
|
||||
type Item = InputMessage;
|
||||
type Error = ClientCoreError;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if buf.len() < LENGHT_ENCODING_PREFIX_SIZE {
|
||||
return Ok(None);
|
||||
}
|
||||
#[allow(clippy::expect_used)]
|
||||
let len = u32::from_le_bytes(buf[0..LENGHT_ENCODING_PREFIX_SIZE].try_into()?) as usize;
|
||||
if buf.len() < len + LENGHT_ENCODING_PREFIX_SIZE {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let decoded = make_bincode_serializer()
|
||||
.deserialize(&buf[LENGHT_ENCODING_PREFIX_SIZE..len + LENGHT_ENCODING_PREFIX_SIZE])?;
|
||||
|
||||
buf.advance(len + LENGHT_ENCODING_PREFIX_SIZE);
|
||||
|
||||
Ok(Some(decoded))
|
||||
}
|
||||
}
|
||||
|
||||
/// Codec for encoding/decoding `ReconstructedMessage` for the AsyncRead interface.
|
||||
///
|
||||
/// This codec was moved from `nymsphinx::receiver` to keep bincode serialization
|
||||
/// out of the core sphinx crate.
|
||||
///
|
||||
/// Uses a simple length-prefixed binary format:
|
||||
/// - 4 bytes: length of encoded message
|
||||
/// - N bytes: encoded message using `ReconstructedMessage::encode()`
|
||||
///
|
||||
/// The message encoding format is:
|
||||
/// - Without sender_tag: `[0][payload...]`
|
||||
/// - With sender_tag: `[1][16-byte tag][payload...]`
|
||||
pub struct ReconstructedMessageCodec;
|
||||
|
||||
impl Encoder<ReconstructedMessage> for ReconstructedMessageCodec {
|
||||
type Error = ClientCoreError;
|
||||
|
||||
fn encode(
|
||||
&mut self,
|
||||
item: ReconstructedMessage,
|
||||
buf: &mut BytesMut,
|
||||
) -> Result<(), Self::Error> {
|
||||
let encoded = item.encode();
|
||||
let encoded_len = encoded.len() as u32;
|
||||
buf.reserve(LENGHT_ENCODING_PREFIX_SIZE + encoded.len());
|
||||
buf.extend_from_slice(&encoded_len.to_le_bytes());
|
||||
buf.extend_from_slice(&encoded);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for ReconstructedMessageCodec {
|
||||
type Item = ReconstructedMessage;
|
||||
type Error = ClientCoreError;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if buf.len() < LENGHT_ENCODING_PREFIX_SIZE {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let len = u32::from_le_bytes(buf[0..LENGHT_ENCODING_PREFIX_SIZE].try_into()?) as usize;
|
||||
|
||||
if buf.len() < len + LENGHT_ENCODING_PREFIX_SIZE {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let decoded = ReconstructedMessage::decode(
|
||||
&buf[LENGHT_ENCODING_PREFIX_SIZE..len + LENGHT_ENCODING_PREFIX_SIZE],
|
||||
)
|
||||
.map_err(|e| ClientCoreError::CodecError(e.to_string()))?;
|
||||
|
||||
buf.advance(len + LENGHT_ENCODING_PREFIX_SIZE);
|
||||
|
||||
Ok(Some(decoded))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use rand::SeedableRng;
|
||||
|
||||
fn test_recipient() -> Recipient {
|
||||
Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap()
|
||||
}
|
||||
|
||||
fn test_sender_tag() -> AnonymousSenderTag {
|
||||
let dummy_seed = [42u8; 32];
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
|
||||
AnonymousSenderTag::new_random(&mut rng)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_decode_all_variants() {
|
||||
let mut codec = InputMessageCodec;
|
||||
{
|
||||
let mut buf = BytesMut::new();
|
||||
let msg = InputMessage::new_anonymous(
|
||||
test_recipient(),
|
||||
vec![1, 2, 3, 4, 5],
|
||||
3,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
);
|
||||
codec.encode(msg, &mut buf).unwrap();
|
||||
let decoded = codec
|
||||
.decode(&mut buf)
|
||||
.unwrap()
|
||||
.expect("Should decode message");
|
||||
|
||||
match decoded {
|
||||
InputMessage::Anonymous {
|
||||
data, reply_surbs, ..
|
||||
} => {
|
||||
assert_eq!(data, vec![1, 2, 3, 4, 5]);
|
||||
assert_eq!(reply_surbs, 3);
|
||||
}
|
||||
_ => panic!("Expected Anonymous variant"),
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut buf = BytesMut::new();
|
||||
let msg = InputMessage::new_reply(
|
||||
test_sender_tag(),
|
||||
vec![6, 7, 8],
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
);
|
||||
codec.encode(msg, &mut buf).unwrap();
|
||||
let decoded = codec
|
||||
.decode(&mut buf)
|
||||
.unwrap()
|
||||
.expect("Should decode message");
|
||||
|
||||
match decoded {
|
||||
InputMessage::Reply { data, .. } => {
|
||||
assert_eq!(data, vec![6, 7, 8]);
|
||||
}
|
||||
_ => panic!("Expected Reply variant"),
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut buf = BytesMut::new();
|
||||
let inner = InputMessage::new_anonymous(
|
||||
test_recipient(),
|
||||
vec![9, 10],
|
||||
2,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
);
|
||||
let msg = InputMessage::new_wrapper(inner, PacketType::Mix);
|
||||
codec.encode(msg, &mut buf).unwrap();
|
||||
let decoded = codec
|
||||
.decode(&mut buf)
|
||||
.unwrap()
|
||||
.expect("Should decode message");
|
||||
|
||||
match decoded {
|
||||
InputMessage::MessageWrapper {
|
||||
message,
|
||||
packet_type,
|
||||
} => {
|
||||
assert_eq!(packet_type, PacketType::Mix);
|
||||
match *message {
|
||||
InputMessage::Anonymous {
|
||||
data, reply_surbs, ..
|
||||
} => {
|
||||
assert_eq!(data, vec![9, 10]);
|
||||
assert_eq!(reply_surbs, 2);
|
||||
}
|
||||
_ => panic!("Expected Anonymous inner message"),
|
||||
}
|
||||
}
|
||||
_ => panic!("Expected MessageWrapper variant"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_decode_sequential_messages() {
|
||||
let mut codec = InputMessageCodec;
|
||||
let mut buf = BytesMut::new();
|
||||
|
||||
codec
|
||||
.encode(
|
||||
InputMessage::new_anonymous(
|
||||
test_recipient(),
|
||||
vec![1, 2, 3],
|
||||
1,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
),
|
||||
&mut buf,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
codec
|
||||
.encode(
|
||||
InputMessage::new_anonymous(
|
||||
test_recipient(),
|
||||
vec![4, 5, 6, 7],
|
||||
2,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
),
|
||||
&mut buf,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
codec
|
||||
.encode(
|
||||
InputMessage::new_anonymous(
|
||||
test_recipient(),
|
||||
vec![8, 9],
|
||||
3,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
),
|
||||
&mut buf,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let decoded1 = codec
|
||||
.decode(&mut buf)
|
||||
.unwrap()
|
||||
.expect("Should decode first message");
|
||||
match decoded1 {
|
||||
InputMessage::Anonymous {
|
||||
data, reply_surbs, ..
|
||||
} => {
|
||||
assert_eq!(data, vec![1, 2, 3]);
|
||||
assert_eq!(reply_surbs, 1);
|
||||
}
|
||||
_ => panic!("Wrong variant"),
|
||||
}
|
||||
|
||||
let decoded2 = codec
|
||||
.decode(&mut buf)
|
||||
.unwrap()
|
||||
.expect("Should decode second message");
|
||||
match decoded2 {
|
||||
InputMessage::Anonymous {
|
||||
data, reply_surbs, ..
|
||||
} => {
|
||||
assert_eq!(data, vec![4, 5, 6, 7]);
|
||||
assert_eq!(reply_surbs, 2);
|
||||
}
|
||||
_ => panic!("Wrong variant"),
|
||||
}
|
||||
|
||||
let decoded3 = codec
|
||||
.decode(&mut buf)
|
||||
.unwrap()
|
||||
.expect("Should decode third message");
|
||||
match decoded3 {
|
||||
InputMessage::Anonymous {
|
||||
data, reply_surbs, ..
|
||||
} => {
|
||||
assert_eq!(data, vec![8, 9]);
|
||||
assert_eq!(reply_surbs, 3);
|
||||
}
|
||||
_ => panic!("Wrong variant"),
|
||||
}
|
||||
|
||||
// Buffer should be empty
|
||||
let decoded4 = codec.decode(&mut buf).unwrap();
|
||||
assert!(decoded4.is_none(), "Should have no more messages");
|
||||
assert_eq!(buf.len(), 0, "Buffer should be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_message_handling() {
|
||||
let mut codec = InputMessageCodec;
|
||||
let mut buf = BytesMut::new();
|
||||
// Empty @ beginning
|
||||
assert!(codec.decode(&mut buf).unwrap().is_none());
|
||||
|
||||
let mut buf = BytesMut::from(&[0x10, 0x00][..]);
|
||||
assert!(codec.decode(&mut buf).unwrap().is_none());
|
||||
assert_eq!(buf.len(), 2, "Buffer should be unchanged");
|
||||
|
||||
let mut full_buf = BytesMut::new();
|
||||
codec
|
||||
.encode(
|
||||
InputMessage::new_anonymous(
|
||||
test_recipient(),
|
||||
vec![1, 2, 3, 4, 5],
|
||||
2,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
),
|
||||
&mut full_buf,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Only first half of the message
|
||||
let partial_len = full_buf.len() / 2;
|
||||
let mut partial_buf = full_buf.split_to(partial_len);
|
||||
|
||||
assert!(codec.decode(&mut partial_buf).unwrap().is_none());
|
||||
assert_eq!(partial_buf.len(), partial_len, "Buffer should be unchanged");
|
||||
|
||||
partial_buf.unsplit(full_buf);
|
||||
let decoded = codec.decode(&mut partial_buf).unwrap();
|
||||
assert!(decoded.is_some(), "Should decode complete message");
|
||||
match decoded.unwrap() {
|
||||
InputMessage::Anonymous { data, .. } => {
|
||||
assert_eq!(data, vec![1, 2, 3, 4, 5]);
|
||||
}
|
||||
_ => panic!("Expected Anonymous variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn addressed_codec_compatibility() {
|
||||
let recipient = test_recipient();
|
||||
let data = b"test message payload";
|
||||
|
||||
let mut addressed_codec = AdressedInputMessageCodec(recipient);
|
||||
let mut buf = BytesMut::new();
|
||||
addressed_codec.encode(data.as_ref(), &mut buf).unwrap();
|
||||
|
||||
let mut input_codec = InputMessageCodec;
|
||||
let decoded = input_codec
|
||||
.decode(&mut buf)
|
||||
.unwrap()
|
||||
.expect("Should decode");
|
||||
|
||||
match decoded {
|
||||
InputMessage::Regular {
|
||||
data: decoded_data,
|
||||
recipient: decoded_recipient,
|
||||
lane,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(decoded_data, data, "Data should match");
|
||||
assert_eq!(decoded_recipient, recipient, "Recipient should match");
|
||||
assert_eq!(lane, TransmissionLane::General, "Should use General lane");
|
||||
}
|
||||
_ => panic!("Expected Regular variant"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-24
@@ -45,25 +45,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_premade_packets(&mut self, packet_bytes: Vec<Vec<u8>>, lane: TransmissionLane) {
|
||||
// Deserialize packet bytes back to MixPacket
|
||||
let packets: Vec<MixPacket> = packet_bytes
|
||||
.into_iter()
|
||||
.filter_map(|bytes| {
|
||||
MixPacket::try_from_v2_bytes(&bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to deserialize premade packet: {}", e);
|
||||
e
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if packets.is_empty() {
|
||||
warn!("No valid premade packets to send");
|
||||
return;
|
||||
}
|
||||
|
||||
async fn handle_premade_packets(&mut self, packets: Vec<MixPacket>, lane: TransmissionLane) {
|
||||
self.message_handler
|
||||
.send_premade_mix_packets(
|
||||
packets
|
||||
@@ -174,9 +156,7 @@ where
|
||||
self.handle_reply(recipient_tag, data, lane, max_retransmissions)
|
||||
.await;
|
||||
}
|
||||
InputMessage::Premade { packet_bytes, lane } => {
|
||||
self.handle_premade_packets(packet_bytes, lane).await
|
||||
}
|
||||
InputMessage::Premade { msgs, lane } => self.handle_premade_packets(msgs, lane).await,
|
||||
InputMessage::MessageWrapper {
|
||||
message,
|
||||
packet_type,
|
||||
@@ -222,8 +202,8 @@ where
|
||||
self.handle_reply(recipient_tag, data, lane, max_retransmissions)
|
||||
.await;
|
||||
}
|
||||
InputMessage::Premade { packet_bytes, lane } => {
|
||||
self.handle_premade_packets(packet_bytes, lane).await
|
||||
InputMessage::Premade { msgs, lane } => {
|
||||
self.handle_premade_packets(msgs, lane).await
|
||||
}
|
||||
// MessageWrappers can't be nested
|
||||
InputMessage::MessageWrapper { .. } => {
|
||||
|
||||
@@ -31,7 +31,7 @@ use tracing::*;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::{sleep, Sleep};
|
||||
|
||||
// use nym_wasm_utils::console_log;
|
||||
// use wasm_utils::console_log;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::tokio::{sleep, Sleep};
|
||||
mod sending_delay_controller;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
use crate::client::inbound_messages::{InputMessage, InputMessageSender};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use futures::StreamExt;
|
||||
use nym_client_core_config_types::StatsReporting;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_statistics_common::clients::{
|
||||
|
||||
@@ -255,15 +255,6 @@ pub enum ClientCoreError {
|
||||
|
||||
#[error("Could not access task registry, {0}")]
|
||||
RegistryAccess(#[from] RegistryAccessError),
|
||||
|
||||
#[error("Serialization error: {0}")]
|
||||
BincodeError(#[from] Box<bincode::ErrorKind>),
|
||||
|
||||
#[error("Could not coarce to array")]
|
||||
ArrayCreationFailure(#[from] std::array::TryFromSliceError),
|
||||
|
||||
#[error("Codec error: {0}")]
|
||||
CodecError(String),
|
||||
}
|
||||
|
||||
impl From<tungstenite::Error> for ClientCoreError {
|
||||
|
||||
@@ -23,8 +23,6 @@ use url::Url;
|
||||
use crate::init::websockets::connect_async;
|
||||
|
||||
use nym_topology::NodeId;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use nym_wasm_utils::websocket::JSWebsocket;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::net::TcpStream;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -34,6 +32,8 @@ use tokio::time::Instant;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_utils::websocket::JSWebsocket;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::std::Instant;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::tokio::sleep;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(deprecated)] // silences clippy warning: use of deprecated associated function `nym_crypto::generic_array::GenericArray::<T, N>::clone_from_slice`: please upgrade to generic-array 1.x - TODO
|
||||
use std::future::Future;
|
||||
|
||||
#[cfg(all(
|
||||
@@ -40,10 +39,3 @@ where
|
||||
{
|
||||
tokio::spawn(future);
|
||||
}
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
bincode::DefaultOptions::new()
|
||||
.with_big_endian()
|
||||
.with_varint_encoding()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(deprecated)] // silences clippy warning: use of deprecated associated function `nym_crypto::generic_array::GenericArray::<T, N>::from_exact_iter`: please upgrade to generic-array 1.x - TODO
|
||||
pub use backend::*;
|
||||
pub use combined::CombinedReplyStorage;
|
||||
pub use key_storage::SentReplyKeys;
|
||||
|
||||
@@ -41,11 +41,11 @@ use std::os::fd::RawFd;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use nym_wasm_utils::websocket::JSWebsocket;
|
||||
#[cfg(not(unix))]
|
||||
use std::os::raw::c_int as RawFd;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_utils::websocket::JSWebsocket;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::tokio::sleep;
|
||||
|
||||
pub mod config;
|
||||
|
||||
@@ -29,7 +29,7 @@ use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use nym_wasm_utils::websocket::JSWebsocket;
|
||||
use wasm_utils::websocket::JSWebsocket;
|
||||
|
||||
// type alias for not having to type the whole thing every single time (and now it makes it easier
|
||||
// to use different types based on compilation target)
|
||||
|
||||
@@ -20,7 +20,7 @@ use nym_api_requests::ecash::{
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ApiHealthResponse, GatewayCoreStatusResponse, HistoricalPerformanceResponse,
|
||||
MixnodeCoreStatusResponse, NymNodeDescriptionV1, NymNodeDescriptionV2,
|
||||
MixnodeCoreStatusResponse, NymNodeDescription,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{
|
||||
NodesByAddressesResponse, SemiSkimmedNodesWithMetadata, SkimmedNode, SkimmedNodesWithMetadata,
|
||||
@@ -273,23 +273,48 @@ impl<C, S> Client<C, S> {
|
||||
Ok(history)
|
||||
}
|
||||
|
||||
#[deprecated(note = "use get_all_cached_described_nodes_v2 instead")]
|
||||
// TODO: combine with NymApiClient...
|
||||
pub async fn get_all_cached_described_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDescriptionV1>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_all_described_nodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_all_cached_described_nodes_v2(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDescriptionV2>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_all_described_nodes_v2().await?)
|
||||
) -> Result<Vec<NymNodeDescription>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut descriptions = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.nym_api.get_nodes_described(Some(page), None).await?;
|
||||
|
||||
descriptions.append(&mut res.data);
|
||||
if descriptions.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
// TODO: combine with NymApiClient...
|
||||
pub async fn get_all_cached_bonded_nym_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
|
||||
self.nym_api.get_all_bonded_nym_nodes().await
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut bonds = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.nym_api.get_nym_nodes(Some(page), None).await?;
|
||||
|
||||
bonds.append(&mut res.data);
|
||||
if bonds.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bonds)
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
@@ -473,10 +498,9 @@ impl NymApiClient {
|
||||
Ok(self.nym_api.health().await?)
|
||||
}
|
||||
|
||||
#[deprecated(note = "use .get_all_described_nodes_v2 instead")]
|
||||
pub async fn get_all_described_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDescriptionV1>, ValidatorClientError> {
|
||||
) -> Result<Vec<NymNodeDescription>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut descriptions = Vec::new();
|
||||
@@ -495,30 +519,6 @@ impl NymApiClient {
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
pub async fn get_all_described_nodes_v2(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDescriptionV2>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut descriptions = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_nodes_described_v2(Some(page), None)
|
||||
.await?;
|
||||
|
||||
descriptions.append(&mut res.data);
|
||||
if descriptions.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
pub async fn get_all_bonded_nym_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
|
||||
|
||||
@@ -17,8 +17,7 @@ use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, ApiHealthResponse, BinaryBuildInformationOwned, ChainBlocksStatusResponse,
|
||||
ChainStatusResponse, KeyRotationInfoResponse, NodePerformanceResponse, NodeRefreshBody,
|
||||
NymNodeDescriptionV1, NymNodeDescriptionV2, PerformanceHistoryResponse, RewardedSetResponse,
|
||||
SignerInformationResponse,
|
||||
NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse, SignerInformationResponse,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{
|
||||
NodesByAddressesRequestBody, NodesByAddressesResponse, PaginatedCachedNodesResponseV1,
|
||||
@@ -117,12 +116,11 @@ pub trait NymApiClientExt: ApiClient {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
#[deprecated(note = "use .get_nodes_described_v2 instead")]
|
||||
async fn get_nodes_described(
|
||||
&self,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedResponse<NymNodeDescriptionV1>, NymAPIError> {
|
||||
) -> Result<PaginatedResponse<NymNodeDescription>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(page) = page {
|
||||
@@ -144,33 +142,6 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_nodes_described_v2(
|
||||
&self,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedResponse<NymNodeDescriptionV2>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(page) = page {
|
||||
params.push(("page", page.to_string()))
|
||||
}
|
||||
|
||||
if let Some(per_page) = per_page {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(
|
||||
&[
|
||||
routes::V2_API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_DESCRIBED,
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_current_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
|
||||
self.get_rewarded_set().await
|
||||
}
|
||||
@@ -302,9 +273,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
|
||||
}
|
||||
|
||||
#[deprecated(note = "use .get_all_described_nodes_v2 instead")]
|
||||
#[allow(deprecated)]
|
||||
async fn get_all_described_nodes(&self) -> Result<Vec<NymNodeDescriptionV1>, NymAPIError> {
|
||||
async fn get_all_described_nodes(&self) -> Result<Vec<NymNodeDescription>, NymAPIError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut descriptions = Vec::new();
|
||||
@@ -323,25 +292,6 @@ pub trait NymApiClientExt: ApiClient {
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
async fn get_all_described_nodes_v2(&self) -> Result<Vec<NymNodeDescriptionV2>, NymAPIError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut descriptions = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.get_nodes_described_v2(Some(page), None).await?;
|
||||
|
||||
descriptions.append(&mut res.data);
|
||||
if descriptions.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_nym_nodes(
|
||||
&self,
|
||||
|
||||
@@ -105,7 +105,7 @@ pub(crate) enum CommonConfigsWrapper {
|
||||
// nym-api
|
||||
NymApi(NymApiConfigLight),
|
||||
|
||||
// anything else that might get introduced
|
||||
// anything else that might get get introduced
|
||||
Unknown(UnknownConfigWrapper),
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Args {
|
||||
}
|
||||
|
||||
pub async fn query(args: Args, client: &QueryClientWithNyxd) {
|
||||
match client.get_all_cached_described_nodes_v2().await {
|
||||
match client.get_all_cached_described_nodes().await {
|
||||
Ok(res) => match args.identity_key {
|
||||
Some(identity_key) => {
|
||||
let node = res.iter().find(|node| {
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Args {
|
||||
}
|
||||
|
||||
pub async fn query(args: Args, client: &QueryClientWithNyxd) {
|
||||
match client.get_all_cached_described_nodes_v2().await {
|
||||
match client.get_all_cached_described_nodes().await {
|
||||
Ok(res) => match args.identity_key {
|
||||
Some(identity_key) => {
|
||||
let node = res.iter().find(|node| {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(clippy::derivable_impls)]
|
||||
// MAX: surpressing warning for the moment, will be dealt with in a different PR (TODO)
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -31,7 +31,6 @@ nym-crypto = { workspace = true, features = ["asymmetric"] }
|
||||
nym-ecash-contract-common = { workspace = true }
|
||||
nym-gateway-requests = { workspace = true }
|
||||
nym-gateway-storage = { workspace = true }
|
||||
nym-metrics = { workspace = true }
|
||||
nym-task = { workspace = true }
|
||||
nym-validator-client = { workspace = true, features = ["http-client"] }
|
||||
nym-upgrade-mode-check = { workspace = true }
|
||||
|
||||
@@ -59,13 +59,9 @@ impl traits::EcashManager for EcashManager {
|
||||
.verify(aggregated_verification_key)
|
||||
.map_err(|err| match err {
|
||||
CompactEcashError::ExpirationDateSignatureValidity => {
|
||||
nym_metrics::inc!("ecash_verification_failures_invalid_date_signature");
|
||||
EcashTicketError::MalformedTicketInvalidDateSignatures
|
||||
}
|
||||
_ => {
|
||||
nym_metrics::inc!("ecash_verification_failures_signature");
|
||||
EcashTicketError::MalformedTicket
|
||||
}
|
||||
_ => EcashTicketError::MalformedTicket,
|
||||
})?;
|
||||
|
||||
self.insert_pay_info(credential.pay_info.into(), insert_index)
|
||||
@@ -174,14 +170,14 @@ impl EcashManager {
|
||||
}
|
||||
|
||||
pub struct MockEcashManager {
|
||||
verification_key: tokio::sync::RwLock<VerificationKeyAuth>,
|
||||
verfication_key: tokio::sync::RwLock<VerificationKeyAuth>,
|
||||
storage: Box<dyn BandwidthGatewayStorage + Send + Sync>,
|
||||
}
|
||||
|
||||
impl MockEcashManager {
|
||||
pub fn new(storage: Box<dyn BandwidthGatewayStorage + Send + Sync>) -> Self {
|
||||
Self {
|
||||
verification_key: tokio::sync::RwLock::new(
|
||||
verfication_key: tokio::sync::RwLock::new(
|
||||
VerificationKeyAuth::from_bytes(&[
|
||||
129, 187, 76, 12, 1, 51, 46, 26, 132, 205, 148, 109, 140, 131, 50, 119, 45,
|
||||
128, 51, 218, 106, 70, 181, 74, 244, 38, 162, 62, 42, 12, 5, 100, 7, 136, 32,
|
||||
@@ -237,7 +233,7 @@ impl traits::EcashManager for MockEcashManager {
|
||||
&self,
|
||||
_epoch_id: EpochId,
|
||||
) -> Result<RwLockReadGuard<'_, VerificationKeyAuth>, EcashTicketError> {
|
||||
Ok(self.verification_key.read().await)
|
||||
Ok(self.verfication_key.read().await)
|
||||
}
|
||||
|
||||
fn storage(&self) -> Box<dyn BandwidthGatewayStorage + Send + Sync> {
|
||||
@@ -253,8 +249,4 @@ impl traits::EcashManager for MockEcashManager {
|
||||
}
|
||||
|
||||
fn async_verify(&self, _ticket: ClientTicket) {}
|
||||
|
||||
fn is_mock(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,13 +222,9 @@ impl SharedState {
|
||||
RwLockReadGuard::try_map(guard, |data| data.get(&epoch_id).map(|d| &d.master_key))
|
||||
{
|
||||
trace!("we already had cached api clients for epoch {epoch_id}");
|
||||
nym_metrics::inc!("ecash_verification_key_cache_hits");
|
||||
return Ok(mapped);
|
||||
}
|
||||
|
||||
// Cache miss - need to fetch and set epoch data
|
||||
nym_metrics::inc!("ecash_verification_key_cache_misses");
|
||||
|
||||
let write_guard = self.set_epoch_data(epoch_id).await?;
|
||||
let guard = write_guard.downgrade();
|
||||
|
||||
|
||||
@@ -20,10 +20,4 @@ pub trait EcashManager {
|
||||
aggregated_verification_key: &VerificationKeyAuth,
|
||||
) -> Result<(), EcashTicketError>;
|
||||
fn async_verify(&self, ticket: ClientTicket);
|
||||
|
||||
/// Returns true if this is a mock ecash manager (for local testing).
|
||||
/// Default implementation returns false.
|
||||
fn is_mock(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use nym_credentials::ecash::utils::{EcashTime, cred_exp_date, ecash_today};
|
||||
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
|
||||
use nym_gateway_requests::models::CredentialSpendingRequest;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use time::{Date, OffsetDateTime};
|
||||
use tracing::*;
|
||||
|
||||
@@ -22,10 +21,6 @@ pub mod ecash;
|
||||
pub mod error;
|
||||
pub mod upgrade_mode;
|
||||
|
||||
// Histogram buckets for ecash verification duration (in seconds)
|
||||
const ECASH_VERIFICATION_DURATION_BUCKETS: &[f64] =
|
||||
&[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0];
|
||||
|
||||
pub struct CredentialVerifier {
|
||||
credential: CredentialSpendingRequest,
|
||||
ecash_verifier: Arc<dyn EcashManager + Send + Sync>,
|
||||
@@ -69,7 +64,6 @@ impl CredentialVerifier {
|
||||
.await?;
|
||||
if spent {
|
||||
trace!("the credential has already been spent before at this gateway");
|
||||
nym_metrics::inc!("ecash_verification_failures_double_spending");
|
||||
return Err(Error::BandwidthCredentialAlreadySpent);
|
||||
}
|
||||
Ok(())
|
||||
@@ -111,9 +105,6 @@ impl CredentialVerifier {
|
||||
}
|
||||
|
||||
pub async fn verify(&mut self) -> Result<i64> {
|
||||
let start = Instant::now();
|
||||
nym_metrics::inc!("ecash_verification_attempts");
|
||||
|
||||
let received_at = OffsetDateTime::now_utc();
|
||||
let spend_date = ecash_today();
|
||||
|
||||
@@ -122,39 +113,15 @@ impl CredentialVerifier {
|
||||
let credential_type = TicketType::try_from_encoded(self.credential.data.payment.t_type)?;
|
||||
|
||||
if self.credential.data.payment.spend_value != 1 {
|
||||
nym_metrics::inc!("ecash_verification_failures_multiple_tickets");
|
||||
return Err(Error::MultipleTickets);
|
||||
}
|
||||
|
||||
if let Err(e) = self.check_credential_spending_date(spend_date.ecash_date()) {
|
||||
nym_metrics::inc!("ecash_verification_failures_invalid_spend_date");
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
self.check_credential_spending_date(spend_date.ecash_date())?;
|
||||
self.check_local_db_for_double_spending(&serial_number)
|
||||
.await?;
|
||||
|
||||
// TODO: do we HAVE TO do it?
|
||||
let verify_result = self.cryptographically_verify_ticket().await;
|
||||
|
||||
// Track verification duration
|
||||
let duration = start.elapsed().as_secs_f64();
|
||||
nym_metrics::add_histogram_obs!(
|
||||
"ecash_verification_duration_seconds",
|
||||
duration,
|
||||
ECASH_VERIFICATION_DURATION_BUCKETS
|
||||
);
|
||||
|
||||
// Track epoch ID - use dynamic metric name via registry
|
||||
let epoch_id = self.credential.data.epoch_id;
|
||||
let epoch_metric = format!(
|
||||
"nym_credential_verification_ecash_epoch_{}_verifications",
|
||||
epoch_id
|
||||
);
|
||||
nym_metrics::metrics_registry().maybe_register_and_inc(&epoch_metric, None);
|
||||
|
||||
// Check verification result after timing
|
||||
verify_result?;
|
||||
self.cryptographically_verify_ticket().await?;
|
||||
|
||||
let ticket_id = self.store_received_ticket(received_at).await?;
|
||||
self.async_verify_ticket(ticket_id);
|
||||
@@ -168,8 +135,6 @@ impl CredentialVerifier {
|
||||
.increase_bandwidth(bandwidth, cred_exp_date())
|
||||
.await?;
|
||||
|
||||
nym_metrics::inc!("ecash_verification_success");
|
||||
|
||||
Ok(self
|
||||
.bandwidth_storage_manager
|
||||
.client_bandwidth
|
||||
|
||||
@@ -12,7 +12,7 @@ description = "Interface for Nym's compact eacash / zknym credential scheme"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nym-bls12_381-fork = { workspace = true }
|
||||
bls12_381 = { workspace = true, default-features = false }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use thiserror::Error;
|
||||
use time::{Date, OffsetDateTime};
|
||||
|
||||
@@ -74,7 +73,7 @@ pub struct CredentialSigningData {
|
||||
pub ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct CredentialSpendingData {
|
||||
pub payment: Payment,
|
||||
|
||||
@@ -87,20 +86,6 @@ pub struct CredentialSpendingData {
|
||||
pub epoch_id: u64,
|
||||
}
|
||||
|
||||
impl Debug for CredentialSpendingData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// we're redacting the payment not since it contains secret,
|
||||
// but because it's producing a lot of noise in the output and
|
||||
// we are not really interested in coordinates of each of the attached curve points
|
||||
f.debug_struct("CredentialSpendingData")
|
||||
.field("payment", &"[REDACTED]")
|
||||
.field("pay_info", &self.pay_info)
|
||||
.field("spend_date", &self.spend_date)
|
||||
.field("epoch_id", &self.epoch_id)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl CredentialSpendingData {
|
||||
pub fn verify(&self, verification_key: &VerificationKeyAuth) -> Result<(), CompactEcashError> {
|
||||
self.payment.spend_verify(
|
||||
|
||||
@@ -4,14 +4,11 @@ version.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Crate for using Nym's zknym credentials"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nym-bls12_381-fork = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
bls12_381 = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
bincode = { workspace = true }
|
||||
cosmrs = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -15,7 +15,6 @@ base64.workspace = true
|
||||
bs58 = { workspace = true }
|
||||
blake3 = { workspace = true, features = ["traits-preview"], optional = true }
|
||||
ctr = { workspace = true, optional = true }
|
||||
curve25519-dalek = { workspace = true, optional = true }
|
||||
digest = { workspace = true, optional = true }
|
||||
generic-array = { workspace = true, optional = true }
|
||||
hkdf = { workspace = true, optional = true }
|
||||
@@ -48,7 +47,7 @@ default = []
|
||||
aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"]
|
||||
naive_jwt = ["asymmetric", "jwt-simple"]
|
||||
serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"]
|
||||
asymmetric = ["x25519-dalek", "ed25519-dalek", "curve25519-dalek", "sha2", "zeroize"]
|
||||
asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"]
|
||||
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array", "sha2", "zeroize"]
|
||||
stream_cipher = ["aes", "ctr", "cipher", "generic-array"]
|
||||
sphinx = ["nym-sphinx-types/sphinx"]
|
||||
|
||||
@@ -20,7 +20,6 @@ pub use serde_helpers::*;
|
||||
#[cfg(feature = "sphinx")]
|
||||
use nym_sphinx_types::{DESTINATION_ADDRESS_LENGTH, DestinationAddressBytes};
|
||||
|
||||
use crate::asymmetric::x25519;
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, Rng, RngCore};
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -111,18 +110,6 @@ impl KeyPair {
|
||||
index: fake_index(pub_bytes),
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts this Ed25519 keypair to an X25519 keypair for ECDH.
|
||||
///
|
||||
/// Uses the standard ed25519→x25519 conversion via SHA-512 hash and clamping.
|
||||
/// This is the same approach as libsodium's `crypto_sign_ed25519_sk_to_curve25519`.
|
||||
///
|
||||
/// # Returns
|
||||
/// The converted X25519 keypair
|
||||
pub fn to_x25519(&self) -> x25519::KeyPair {
|
||||
let private_key = self.private_key.to_x25519();
|
||||
x25519::KeyPair::from(private_key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reduces a byte slice into a u32 value by XOR-ing all its bytes into a 4-byte accumulator.
|
||||
@@ -149,16 +136,6 @@ impl From<PrivateKey> for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(PrivateKey, PublicKey)> for KeyPair {
|
||||
fn from((private_key, public_key): (PrivateKey, PublicKey)) -> Self {
|
||||
KeyPair {
|
||||
private_key,
|
||||
public_key,
|
||||
index: fake_index(public_key.to_bytes().as_ref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = PrivateKey;
|
||||
type PublicPemKey = PublicKey;
|
||||
@@ -208,25 +185,14 @@ impl PublicKey {
|
||||
}
|
||||
|
||||
/// Convert this public key to a byte array.
|
||||
#[inline]
|
||||
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_LENGTH] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
/// View this public key as a byte array.
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Self, Ed25519RecoveryError> {
|
||||
Self::from_byte_array(b.try_into()?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_byte_array(b: &[u8; PUBLIC_KEY_LENGTH]) -> Result<Self, Ed25519RecoveryError> {
|
||||
Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(b)?))
|
||||
Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(
|
||||
b.try_into()?,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub fn to_base58_string(self) -> String {
|
||||
@@ -247,37 +213,6 @@ impl PublicKey {
|
||||
) -> Result<(), SignatureError> {
|
||||
self.0.verify(message.as_ref(), &signature.0)
|
||||
}
|
||||
|
||||
/// Converts this Ed25519 public key to an X25519 public key for ECDH.
|
||||
///
|
||||
/// Uses the standard ed25519→x25519 conversion by converting the Edwards point
|
||||
/// to Montgomery form. This is the same approach as libsodium's
|
||||
/// `crypto_sign_ed25519_pk_to_curve25519`.
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Ok(x25519::PublicKey)` - The converted X25519 public key
|
||||
/// * `Err(Ed25519RecoveryError)` - If the conversion fails (e.g., low-order point)
|
||||
pub fn to_x25519(&self) -> Result<crate::asymmetric::x25519::PublicKey, Ed25519RecoveryError> {
|
||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||
|
||||
// Decompress the Ed25519 point
|
||||
let compressed = CompressedEdwardsY((*self).to_bytes());
|
||||
let edwards_point = compressed.decompress().ok_or_else(|| {
|
||||
Ed25519RecoveryError::MalformedBytes(SignatureError::from_source(
|
||||
"Failed to decompress Ed25519 point".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
// Convert to Montgomery form
|
||||
let montgomery = edwards_point.to_montgomery();
|
||||
|
||||
// Create X25519 public key
|
||||
crate::asymmetric::x25519::PublicKey::from_bytes(montgomery.as_bytes()).map_err(|_| {
|
||||
Ed25519RecoveryError::MalformedBytes(SignatureError::from_source(
|
||||
"Failed to convert to X25519".to_string(),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sphinx")]
|
||||
@@ -399,30 +334,6 @@ impl PrivateKey {
|
||||
let signature_bytes = self.sign(text).to_bytes();
|
||||
bs58::encode(signature_bytes).into_string()
|
||||
}
|
||||
|
||||
/// Converts this Ed25519 private key to an X25519 private key for ECDH.
|
||||
///
|
||||
/// Uses the standard ed25519→x25519 conversion via SHA-512 hash and clamping.
|
||||
/// This is the same approach as libsodium's `crypto_sign_ed25519_sk_to_curve25519`.
|
||||
///
|
||||
/// # Returns
|
||||
/// The converted X25519 private key
|
||||
pub fn to_x25519(&self) -> crate::asymmetric::x25519::PrivateKey {
|
||||
use sha2::{Digest, Sha512};
|
||||
|
||||
// Hash the Ed25519 secret key with SHA-512
|
||||
// Both hash and x25519_bytes wrapped in Zeroizing to clear key material
|
||||
let mut hash = zeroize::Zeroizing::new([0u8; 64]);
|
||||
hash.copy_from_slice(&Sha512::digest(self.0));
|
||||
|
||||
// Take first 32 bytes (clamping is done automatically by x25519_dalek::StaticSecret)
|
||||
let mut x25519_bytes = zeroize::Zeroizing::new([0u8; 32]);
|
||||
x25519_bytes.copy_from_slice(&hash[..32]);
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
crate::asymmetric::x25519::PrivateKey::from_bytes(&*x25519_bytes)
|
||||
.expect("x25519 key conversion should never fail")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -606,27 +517,4 @@ mod tests {
|
||||
|
||||
assert_eq!(sig1.to_vec(), sig2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rand")]
|
||||
fn test_ed25519_to_x25519_ecdh() {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// Create two ed25519 keypairs
|
||||
let alice_ed = KeyPair::new(&mut rng);
|
||||
let bob_ed = KeyPair::new(&mut rng);
|
||||
|
||||
// Convert to x25519
|
||||
let alice_x25519_private = alice_ed.private_key().to_x25519();
|
||||
let alice_x25519_public = alice_ed.public_key().to_x25519().unwrap();
|
||||
let bob_x25519_private = bob_ed.private_key().to_x25519();
|
||||
let bob_x25519_public = bob_ed.public_key().to_x25519().unwrap();
|
||||
|
||||
// Perform ECDH both ways
|
||||
let alice_shared = alice_x25519_private.diffie_hellman(&bob_x25519_public);
|
||||
let bob_shared = bob_x25519_private.diffie_hellman(&alice_x25519_public);
|
||||
|
||||
// Both should produce the same shared secret
|
||||
assert_eq!(alice_shared, bob_shared);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
use base64::Engine;
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
@@ -57,15 +56,6 @@ pub struct KeyPair {
|
||||
pub(crate) public_key: PublicKey,
|
||||
}
|
||||
|
||||
impl Debug for KeyPair {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("KeyPair")
|
||||
.field("private_key", &"<redacted>")
|
||||
.field("public_key", &self.public_key.to_base58_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
@@ -103,15 +93,6 @@ impl From<PrivateKey> for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(PrivateKey, PublicKey)> for KeyPair {
|
||||
fn from((private_key, public_key): (PrivateKey, PublicKey)) -> Self {
|
||||
KeyPair {
|
||||
private_key,
|
||||
public_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = PrivateKey;
|
||||
type PublicPemKey = PublicKey;
|
||||
@@ -135,13 +116,6 @@ impl PemStorableKeyPair for KeyPair {
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub struct PublicKey(x25519_dalek::PublicKey);
|
||||
|
||||
impl Deref for PublicKey {
|
||||
type Target = x25519_dalek::PublicKey;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&self.to_base58_string(), f)
|
||||
@@ -155,17 +129,14 @@ impl Debug for PublicKey {
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
#[inline]
|
||||
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_SIZE] {
|
||||
*self.0.as_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Self, KeyRecoveryError> {
|
||||
if b.len() != PUBLIC_KEY_SIZE {
|
||||
return Err(KeyRecoveryError::InvalidSizePublicKey {
|
||||
@@ -175,12 +146,7 @@ impl PublicKey {
|
||||
}
|
||||
let mut bytes = [0; PUBLIC_KEY_SIZE];
|
||||
bytes.copy_from_slice(&b[..PUBLIC_KEY_SIZE]);
|
||||
Ok(Self::from_byte_array(&bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_byte_array(b: &[u8; PUBLIC_KEY_SIZE]) -> Self {
|
||||
Self(x25519_dalek::PublicKey::from(*b))
|
||||
Ok(Self(x25519_dalek::PublicKey::from(bytes)))
|
||||
}
|
||||
|
||||
pub fn to_base58_string(self) -> String {
|
||||
@@ -208,12 +174,6 @@ impl PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; PUBLIC_KEY_SIZE]> for PublicKey {
|
||||
fn from(bytes: [u8; PUBLIC_KEY_SIZE]) -> Self {
|
||||
PublicKey(x25519_dalek::PublicKey::from(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PublicKey {
|
||||
type Err = KeyRecoveryError;
|
||||
|
||||
@@ -336,10 +296,6 @@ impl PrivateKey {
|
||||
Ok(Self(x25519_dalek::StaticSecret::from(bytes)))
|
||||
}
|
||||
|
||||
pub fn from_secret(secret: [u8; PRIVATE_KEY_SIZE]) -> Self {
|
||||
Self(x25519_dalek::StaticSecret::from(secret))
|
||||
}
|
||||
|
||||
pub fn to_base58_string(&self) -> String {
|
||||
bs58::encode(&self.to_bytes()).into_string()
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Key Derivation Functions using Blake3.
|
||||
|
||||
/// Derives a 32-byte key using Blake3's key derivation mode.
|
||||
///
|
||||
/// Uses Blake3's built-in `derive_key` function with domain separation via context string.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `context` - Context string for domain separation (e.g., "nym-lp-psk-v1")
|
||||
/// * `key_material` - Input key material (shared secret from ECDH, etc.)
|
||||
/// * `salt` - Additional salt for freshness (timestamp + nonce)
|
||||
///
|
||||
/// # Returns
|
||||
/// 32-byte derived key suitable for use as PSK
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let psk = derive_key_blake3("nym-lp-psk-v1", shared_secret.as_bytes(), &salt);
|
||||
/// ```
|
||||
pub fn derive_key_blake3(context: &str, key_material: &[u8], salt: &[u8]) -> [u8; 32] {
|
||||
// Concatenate key_material and salt as input
|
||||
let input = [key_material, salt].concat();
|
||||
|
||||
// Use Blake3's derive_key with context for domain separation
|
||||
// blake3::derive_key returns [u8; 32] directly
|
||||
blake3::derive_key(context, &input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_deterministic_derivation() {
|
||||
let context = "test-context";
|
||||
let key_material = b"shared_secret_12345";
|
||||
let salt = b"salt_67890";
|
||||
|
||||
let key1 = derive_key_blake3(context, key_material, salt);
|
||||
let key2 = derive_key_blake3(context, key_material, salt);
|
||||
|
||||
assert_eq!(key1, key2, "Same inputs should produce same output");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_contexts_produce_different_keys() {
|
||||
let key_material = b"shared_secret";
|
||||
let salt = b"salt";
|
||||
|
||||
let key1 = derive_key_blake3("context1", key_material, salt);
|
||||
let key2 = derive_key_blake3("context2", key_material, salt);
|
||||
|
||||
assert_ne!(
|
||||
key1, key2,
|
||||
"Different contexts should produce different keys"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_salts_produce_different_keys() {
|
||||
let context = "test-context";
|
||||
let key_material = b"shared_secret";
|
||||
|
||||
let key1 = derive_key_blake3(context, key_material, b"salt1");
|
||||
let key2 = derive_key_blake3(context, key_material, b"salt2");
|
||||
|
||||
assert_ne!(key1, key2, "Different salts should produce different keys");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_key_material_produces_different_keys() {
|
||||
let context = "test-context";
|
||||
let salt = b"salt";
|
||||
|
||||
let key1 = derive_key_blake3(context, b"secret1", salt);
|
||||
let key2 = derive_key_blake3(context, b"secret2", salt);
|
||||
|
||||
assert_ne!(
|
||||
key1, key2,
|
||||
"Different key material should produce different keys"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_output_length() {
|
||||
let key = derive_key_blake3("test", b"key", b"salt");
|
||||
assert_eq!(key.len(), 32, "Output should be exactly 32 bytes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_inputs() {
|
||||
// Should not panic with empty inputs
|
||||
let key = derive_key_blake3("test", b"", b"");
|
||||
assert_eq!(key.len(), 32);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(deprecated)] // silences clippy warning: deprecated associated function `generic_array::GenericArray::<T, N>::from_exact_iter`: please upgrade to generic-array 1.x - TODO
|
||||
|
||||
#[cfg(feature = "asymmetric")]
|
||||
pub mod asymmetric;
|
||||
pub mod bech32_address_validation;
|
||||
@@ -12,8 +10,6 @@ pub mod crypto_hash;
|
||||
pub mod hkdf;
|
||||
#[cfg(feature = "hashing")]
|
||||
pub mod hmac;
|
||||
#[cfg(feature = "hashing")]
|
||||
pub mod kdf;
|
||||
#[cfg(all(feature = "asymmetric", feature = "hashing", feature = "stream_cipher"))]
|
||||
pub mod shared_key;
|
||||
pub mod symmetric;
|
||||
|
||||
@@ -5,18 +5,15 @@ edition = "2021"
|
||||
resolver = "2"
|
||||
license.workspace = true
|
||||
description = "Nym's Distributed Key Generation functionality"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitvec = { workspace = true }
|
||||
|
||||
# unfortunately until https://github.com/zkcrypto/nym-bls12_381-fork/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
|
||||
nym-bls12_381-fork = { workspace = true, features = ["alloc", "pairings", "experimental", "zeroize"] }
|
||||
bls12_381 = { workspace = true, default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
|
||||
nym-contracts-common = { workspace = true, optional = true }
|
||||
bs58 = { workspace = true }
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bls12_381::{G1Projective, G2Affine, G2Prepared, Scalar};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use ff::Field;
|
||||
use nym_bls12_381_fork::{G1Projective, G2Affine, G2Prepared, Scalar};
|
||||
use nym_dkg::bte::encryption::BabyStepGiantStepLookup;
|
||||
use nym_dkg::bte::proof_chunking::ProofOfChunking;
|
||||
use nym_dkg::bte::proof_discrete_log::ProofOfDiscreteLog;
|
||||
|
||||
@@ -6,9 +6,9 @@ use crate::bte::{evaluate_f, Params, CHUNK_SIZE, G2_GENERATOR_PREPARED, NUM_CHUN
|
||||
use crate::error::DkgError;
|
||||
use crate::utils::{combine_g1_chunks, combine_scalar_chunks, deserialize_g1, deserialize_g2};
|
||||
use crate::{Chunk, ChunkedShare, Share};
|
||||
use bls12_381::{G1Affine, G1Projective, G2Prepared, G2Projective, Gt, Scalar};
|
||||
use ff::Field;
|
||||
use group::{Curve, Group, GroupEncoding};
|
||||
use nym_bls12_381_fork::{G1Affine, G1Projective, G2Prepared, G2Projective, Gt, Scalar};
|
||||
use rand::CryptoRng;
|
||||
use rand_core::RngCore;
|
||||
use std::collections::HashMap;
|
||||
@@ -43,7 +43,7 @@ impl Ciphertexts {
|
||||
// which is equivalent to checking whether e(R_j, f) • e(S_i, h) • e(g1, Z_i)^-1 == id
|
||||
// and due to bilinear property whether e(R_j, f) • e(S_i, h) • e(g1^-1, Z_i) == id
|
||||
for i in 0..self.rr.len() {
|
||||
let miller = nym_bls12_381_fork::multi_miller_loop(&[
|
||||
let miller = bls12_381::multi_miller_loop(&[
|
||||
(&self.rr[i].to_affine(), &f_prepared),
|
||||
(&self.ss[i].to_affine(), ¶ms._h_prepared),
|
||||
(&g1_neg, &G2Prepared::from(self.zz[i].to_affine())),
|
||||
@@ -294,7 +294,7 @@ pub fn decrypt_share(
|
||||
let zz_j = ciphertext.zz[j].to_affine();
|
||||
let cc_ij = &ciphertext.ciphertext_chunks[i][j];
|
||||
|
||||
let miller = nym_bls12_381_fork::multi_miller_loop(&[
|
||||
let miller = bls12_381::multi_miller_loop(&[
|
||||
(&cc_ij.to_affine(), &G2_GENERATOR_PREPARED),
|
||||
(&rr_j.to_affine(), &G2Prepared::from(b_neg)),
|
||||
(&dk.a.to_affine(), &G2Prepared::from(zz_j)),
|
||||
|
||||
@@ -5,9 +5,9 @@ use crate::bte::proof_discrete_log::ProofOfDiscreteLog;
|
||||
use crate::bte::Params;
|
||||
use crate::error::DkgError;
|
||||
use crate::utils::{deserialize_g1, deserialize_g2, deserialize_scalar};
|
||||
use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||
use ff::Field;
|
||||
use group::GroupEncoding;
|
||||
use nym_bls12_381_fork::{G1Projective, G2Projective, Scalar};
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use rand::CryptoRng;
|
||||
use rand_core::RngCore;
|
||||
|
||||
@@ -6,8 +6,8 @@ use std::sync::LazyLock;
|
||||
use crate::bte::encryption::BabyStepGiantStepLookup;
|
||||
use crate::utils::hash_g2;
|
||||
use crate::{Chunk, Share};
|
||||
use bls12_381::{G1Affine, G2Affine, G2Prepared, G2Projective, Gt};
|
||||
use group::Curve;
|
||||
use nym_bls12_381_fork::{G1Affine, G2Affine, G2Prepared, G2Projective, Gt};
|
||||
|
||||
pub mod encryption;
|
||||
pub mod keys;
|
||||
@@ -19,7 +19,7 @@ pub use encryption::{decrypt_share, encrypt_shares, Ciphertexts};
|
||||
pub use keys::{keygen, DecryptionKey, PublicKey, PublicKeyWithProof};
|
||||
|
||||
pub(crate) static PAIRING_BASE: LazyLock<Gt> =
|
||||
LazyLock::new(|| nym_bls12_381_fork::pairing(&G1Affine::generator(), &G2Affine::generator()));
|
||||
LazyLock::new(|| bls12_381::pairing(&G1Affine::generator(), &G2Affine::generator()));
|
||||
pub(crate) static G2_GENERATOR_PREPARED: LazyLock<G2Prepared> =
|
||||
LazyLock::new(|| G2Prepared::from(G2Affine::generator()));
|
||||
pub static BSGS_TABLE: LazyLock<BabyStepGiantStepLookup> =
|
||||
|
||||
@@ -7,9 +7,9 @@ use crate::ensure_len;
|
||||
use crate::error::DkgError;
|
||||
use crate::utils::{deserialize_g1, hash_to_scalar};
|
||||
use crate::utils::{deserialize_scalar, RandomOracleBuilder};
|
||||
use bls12_381::{G1Projective, Scalar};
|
||||
use ff::Field;
|
||||
use group::{Group, GroupEncoding};
|
||||
use nym_bls12_381_fork::{G1Projective, Scalar};
|
||||
use rand::{CryptoRng, Rng};
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::utils::hash_to_scalar;
|
||||
use bls12_381::{G1Projective, Scalar};
|
||||
use ff::Field;
|
||||
use group::GroupEncoding;
|
||||
use nym_bls12_381_fork::{G1Projective, Scalar};
|
||||
use rand::CryptoRng;
|
||||
use rand_core::RngCore;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
@@ -6,9 +6,9 @@ use crate::error::DkgError;
|
||||
use crate::interpolation::polynomial::PublicCoefficients;
|
||||
use crate::utils::{deserialize_g1, deserialize_g2, deserialize_scalar, hash_to_scalar};
|
||||
use crate::{NodeIndex, Share};
|
||||
use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||
use ff::Field;
|
||||
use group::GroupEncoding;
|
||||
use nym_bls12_381_fork::{G1Projective, G2Projective, Scalar};
|
||||
use rand::CryptoRng;
|
||||
use rand_core::RngCore;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -11,8 +11,8 @@ use crate::interpolation::{
|
||||
};
|
||||
use crate::utils::deserialize_g2;
|
||||
use crate::{NodeIndex, Share, Threshold};
|
||||
use bls12_381::{G2Projective, Scalar};
|
||||
use group::GroupEncoding;
|
||||
use nym_bls12_381_fork::{G2Projective, Scalar};
|
||||
use rand::CryptoRng;
|
||||
use rand_core::RngCore;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::DkgError;
|
||||
use bls12_381::Scalar;
|
||||
use core::iter::Sum;
|
||||
use core::ops::Mul;
|
||||
use nym_bls12_381_fork::Scalar;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub mod polynomial;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
use crate::error::DkgError;
|
||||
use crate::utils::deserialize_g2;
|
||||
use bls12_381::{G2Projective, Scalar};
|
||||
use ff::Field;
|
||||
use group::GroupEncoding;
|
||||
use nym_bls12_381_fork::{G2Projective, Scalar};
|
||||
use rand::CryptoRng;
|
||||
use rand_core::RngCore;
|
||||
use std::ops::{Add, Index, IndexMut};
|
||||
|
||||
@@ -13,8 +13,8 @@ pub mod dealing;
|
||||
pub(crate) mod share;
|
||||
pub(crate) mod utils;
|
||||
|
||||
pub use bls12_381::{G2Projective, Scalar};
|
||||
pub use dealing::*;
|
||||
pub use nym_bls12_381_fork::{G2Projective, Scalar};
|
||||
pub use share::*;
|
||||
|
||||
// TODO: presumably this should live in a some different, common, crate?
|
||||
@@ -25,7 +25,7 @@ pub type NodeIndex = u64;
|
||||
mod tests {
|
||||
use crate::interpolation::perform_lagrangian_interpolation_at_origin;
|
||||
use crate::interpolation::polynomial::Polynomial;
|
||||
use nym_bls12_381_fork::Scalar;
|
||||
use bls12_381::Scalar;
|
||||
use rand_chacha::rand_core::SeedableRng;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::bte::{CHUNK_BYTES, NUM_CHUNKS, SCALAR_SIZE};
|
||||
use crate::error::DkgError;
|
||||
use crate::interpolation::perform_lagrangian_interpolation_at_origin;
|
||||
use crate::NodeIndex;
|
||||
use nym_bls12_381_fork::Scalar;
|
||||
use bls12_381::Scalar;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
// if this type is changed, one must ensure all values can fit in it
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::bte::CHUNK_SIZE;
|
||||
use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve, HashToField};
|
||||
use bls12_381::G1Projective;
|
||||
use bls12_381::{G2Projective, Scalar};
|
||||
use group::GroupEncoding;
|
||||
use nym_bls12_381_fork::hash_to_curve::{ExpandMsgXmd, HashToCurve, HashToField};
|
||||
use nym_bls12_381_fork::G1Projective;
|
||||
use nym_bls12_381_fork::{G2Projective, Scalar};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[macro_export]
|
||||
@@ -116,7 +116,7 @@ pub(crate) fn deserialize_g2(b: &[u8]) -> Option<G2Projective> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_bls12_381_fork::G2Affine;
|
||||
use bls12_381::G2Affine;
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_scalar() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_bls12_381_fork::{G2Projective, Scalar};
|
||||
use bls12_381::{G2Projective, Scalar};
|
||||
use nym_dkg::bte::{decrypt_share, keygen, setup};
|
||||
use nym_dkg::dealing::RecoveredVerificationKeys;
|
||||
use nym_dkg::interpolation::perform_lagrangian_interpolation_at_origin;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#![allow(deprecated)] // silences clippy warning: deprecated associated function `nym_crypto::generic_array::GenericArray::<T, N>::clone_from_slice`: please upgrade to generic-array 1.x - TODO
|
||||
|
||||
pub use nym_crypto::generic_array;
|
||||
use nym_crypto::OutputSizeUser;
|
||||
|
||||
@@ -70,7 +70,7 @@ impl BinaryRequest {
|
||||
|
||||
let plaintext = match self {
|
||||
BinaryRequest::ForwardSphinx { packet } => packet.into_v1_bytes()?,
|
||||
BinaryRequest::ForwardSphinxV2 { packet } => packet.to_v2_bytes()?,
|
||||
BinaryRequest::ForwardSphinxV2 { packet } => packet.into_v2_bytes()?,
|
||||
};
|
||||
|
||||
BinaryData::make_encrypted_blob(kind as u8, &plaintext, shared_key)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
ALTER TABLE wireguard_peer
|
||||
ADD COLUMN psk VARCHAR;
|
||||
@@ -133,13 +133,6 @@ impl GatewayStorage {
|
||||
}
|
||||
};
|
||||
|
||||
Self::from_connection_pool(connection_pool, message_retrieval_limit).await
|
||||
}
|
||||
|
||||
pub async fn from_connection_pool(
|
||||
connection_pool: sqlx::sqlite::SqlitePool,
|
||||
message_retrieval_limit: i64,
|
||||
) -> Result<Self, GatewayStorageError> {
|
||||
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
|
||||
error!("Failed to perform migration on the SQLx database: {err}");
|
||||
return Err(err.into());
|
||||
@@ -577,21 +570,4 @@ impl BandwidthGatewayStorage for GatewayStorage {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the stored PSK of the wireguard peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: the unique public key of the wireguard peer.
|
||||
/// * `psk`: the PSK of the wireguard peer.
|
||||
async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), GatewayStorageError> {
|
||||
self.wireguard_peer_manager
|
||||
.update_peer_psk(public_key, psk)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{error::GatewayStorageError, make_bincode_serializer};
|
||||
use defguard_wireguard_rs::key::Key;
|
||||
use nym_credentials_interface::{AvailableBandwidth, ClientTicket, CredentialSpendingData};
|
||||
use nym_gateway_requests::shared_key::SharedSymmetricKey;
|
||||
use sqlx::FromRow;
|
||||
@@ -96,7 +95,6 @@ pub struct WireguardPeer {
|
||||
pub public_key: String,
|
||||
pub allowed_ips: Vec<u8>,
|
||||
pub client_id: i64,
|
||||
pub psk: Option<String>,
|
||||
}
|
||||
|
||||
impl WireguardPeer {
|
||||
@@ -112,7 +110,6 @@ impl WireguardPeer {
|
||||
source,
|
||||
})?,
|
||||
client_id,
|
||||
psk: value.preshared_key.map(|psk| psk.to_lower_hex()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -135,11 +132,6 @@ impl TryFrom<WireguardPeer> for defguard_wireguard_rs::host::Peer {
|
||||
field_key: "allowed_ips",
|
||||
source,
|
||||
})?,
|
||||
preshared_key: value
|
||||
.psk
|
||||
.map(|psk| Key::decode(&psk))
|
||||
.transpose()
|
||||
.map_err(|_| Self::Error::TypeConversion { field_key: "psk" })?,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -170,18 +170,6 @@ pub trait BandwidthGatewayStorage: dyn_clone::DynClone {
|
||||
/// * `peer_public_key`: wireguard public key of the peer to be removed.
|
||||
async fn remove_wireguard_peer(&self, peer_public_key: &str)
|
||||
-> Result<(), GatewayStorageError>;
|
||||
|
||||
/// Update the stored PSK of the wireguard peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: the unique public key of the wireguard peer.
|
||||
/// * `psk`: the PSK of the wireguard peer.
|
||||
async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), GatewayStorageError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "mock")]
|
||||
@@ -519,16 +507,5 @@ pub mod mock {
|
||||
self.write().await.wireguard_peers.remove(peer_public_key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), GatewayStorageError> {
|
||||
if let Some(peer) = self.write().await.wireguard_peers.get_mut(public_key) {
|
||||
peer.psk = psk.map(|psk| psk.to_owned())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,29 +98,4 @@ impl WgPeerManager {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the stored PSK of the wireguard peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: the unique public key of the wireguard peer.
|
||||
/// * `psk`: the PSK of the wireguard peer.
|
||||
pub(crate) async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE wireguard_peer
|
||||
SET psk = ?
|
||||
WHERE public_key = ?
|
||||
"#,
|
||||
psk,
|
||||
public_key,
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ proc-macro-crate = "3"
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
|
||||
[dev-dependencies]
|
||||
nym-http-api-client = { workspace = true }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
reqwest = { workspace = true }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -51,9 +51,6 @@ pub const VERCEL_COM_IPS: &[IpAddr] = &[
|
||||
IpAddr::V4(Ipv4Addr::new(198, 169, 1, 193)),
|
||||
];
|
||||
|
||||
pub const NYM_API_CDN: &str = "cdn1.media-platform.net";
|
||||
pub const NYM_API_CDN_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(172, 104, 178, 252))];
|
||||
|
||||
pub const NYM_COM_DOMAIN: &str = "nym.com";
|
||||
pub const NYM_COM_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(76, 76, 21, 22))];
|
||||
|
||||
@@ -91,7 +88,6 @@ pub fn default_static_addrs() -> HashMap<String, Vec<IpAddr>> {
|
||||
m.insert(YELP_FASTLY_DOMAIN.to_string(), YELP_FASTLY_IPS.to_vec());
|
||||
m.insert(VERCEL_APP_DOMAIN.to_string(), VERCEL_APP_IPS.to_vec());
|
||||
m.insert(VERCEL_COM_DOMAIN.to_string(), VERCEL_COM_IPS.to_vec());
|
||||
m.insert(NYM_API_CDN.to_string(), NYM_API_CDN_IPS.to_vec());
|
||||
m.insert(NYM_COM_DOMAIN.to_string(), NYM_COM_IPS.to_vec());
|
||||
m.insert(NYM_STATS_API_DOMAIN.to_string(), NYM_STATS_API_IPS.to_vec());
|
||||
m.insert(NYM_RPC_DOMAIN.to_string(), NYM_RPC_IPS.to_vec());
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(deprecated)]
|
||||
// silences clippy warning: use of deprecated tuple variant `HttpClientError::GenericRequestFailure`: use another more strongly typed variant - this variant is only left for compatibility reasons - TODO
|
||||
|
||||
//! Nym HTTP API Client
|
||||
//!
|
||||
//! Centralizes and implements the core API client functionality. This crate provides custom,
|
||||
@@ -332,14 +329,14 @@ pub enum HttpClientError {
|
||||
|
||||
#[error("failed to send request for {url}: {source}")]
|
||||
RequestSendFailure {
|
||||
url: Box<reqwest::Url>,
|
||||
url: reqwest::Url,
|
||||
#[source]
|
||||
source: ReqwestErrorWrapper,
|
||||
},
|
||||
|
||||
#[error("failed to read response body from {url}: {source}")]
|
||||
ResponseReadFailure {
|
||||
url: Box<reqwest::Url>,
|
||||
url: reqwest::Url,
|
||||
headers: Box<HeaderMap>,
|
||||
status: StatusCode,
|
||||
#[source]
|
||||
@@ -356,7 +353,7 @@ pub enum HttpClientError {
|
||||
},
|
||||
|
||||
#[error("the requested resource could not be found at {url}")]
|
||||
NotFound { url: Box<reqwest::Url> },
|
||||
NotFound { url: reqwest::Url },
|
||||
|
||||
#[error("attempted to use domain fronting and clone a request containing stream data")]
|
||||
AttemptedToCloneStreamRequest,
|
||||
@@ -368,7 +365,7 @@ pub enum HttpClientError {
|
||||
"the request for {url} failed with status '{status}'. no additional error message provided. response headers: {headers:?}"
|
||||
)]
|
||||
RequestFailure {
|
||||
url: Box<reqwest::Url>,
|
||||
url: reqwest::Url,
|
||||
status: StatusCode,
|
||||
headers: Box<HeaderMap>,
|
||||
},
|
||||
@@ -377,7 +374,7 @@ pub enum HttpClientError {
|
||||
"the returned response from {url} was empty. status: '{status}'. response headers: {headers:?}"
|
||||
)]
|
||||
EmptyResponse {
|
||||
url: Box<reqwest::Url>,
|
||||
url: reqwest::Url,
|
||||
status: StatusCode,
|
||||
headers: Box<HeaderMap>,
|
||||
},
|
||||
@@ -386,7 +383,7 @@ pub enum HttpClientError {
|
||||
"failed to resolve request for {url}. status: '{status}'. response headers: {headers:?}. additional error message: {error}"
|
||||
)]
|
||||
EndpointFailure {
|
||||
url: Box<reqwest::Url>,
|
||||
url: reqwest::Url,
|
||||
status: StatusCode,
|
||||
headers: Box<HeaderMap>,
|
||||
error: String,
|
||||
@@ -456,7 +453,7 @@ impl HttpClientError {
|
||||
|
||||
pub fn request_send_error(url: reqwest::Url, source: reqwest::Error) -> Self {
|
||||
HttpClientError::RequestSendFailure {
|
||||
url: Box::new(url),
|
||||
url,
|
||||
source: ReqwestErrorWrapper(source),
|
||||
}
|
||||
}
|
||||
@@ -1520,7 +1517,7 @@ where
|
||||
|
||||
if !allow_empty && let Some(0) = res.content_length() {
|
||||
return Err(HttpClientError::EmptyResponse {
|
||||
url: Box::new(url),
|
||||
url,
|
||||
status,
|
||||
headers: Box::new(headers),
|
||||
});
|
||||
@@ -1533,25 +1530,25 @@ where
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|source| HttpClientError::ResponseReadFailure {
|
||||
url: Box::new(url),
|
||||
url,
|
||||
headers: Box::new(headers.clone()),
|
||||
status,
|
||||
source: ReqwestErrorWrapper(source),
|
||||
})?;
|
||||
decode_raw_response(&headers, full)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(HttpClientError::NotFound { url: Box::new(url) })
|
||||
Err(HttpClientError::NotFound { url })
|
||||
} else {
|
||||
let Ok(plaintext) = res.text().await else {
|
||||
return Err(HttpClientError::RequestFailure {
|
||||
url: Box::new(url),
|
||||
url,
|
||||
status,
|
||||
headers: Box::new(headers),
|
||||
});
|
||||
};
|
||||
|
||||
Err(HttpClientError::EndpointFailure {
|
||||
url: Box::new(url),
|
||||
url,
|
||||
status,
|
||||
headers: Box::new(headers),
|
||||
error: plaintext,
|
||||
|
||||
@@ -11,10 +11,8 @@ pub mod response;
|
||||
#[cfg(feature = "output")]
|
||||
pub use response::*;
|
||||
|
||||
pub use ::bincode::Options as BincodeOptions;
|
||||
|
||||
// be explicit about those values because bincode uses different defaults in different places
|
||||
pub fn make_bincode_serializer() -> impl BincodeOptions {
|
||||
pub fn make_bincode_serializer() -> impl ::bincode::Options {
|
||||
use ::bincode::Options;
|
||||
::bincode::DefaultOptions::new()
|
||||
.with_little_endian()
|
||||
|
||||
@@ -150,10 +150,6 @@ impl OutputParams {
|
||||
pub fn get_output(&self) -> Output {
|
||||
self.output.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn to_response<T: Serialize>(self, data: T) -> FormattedResponse<T> {
|
||||
self.get_output().to_response(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Output {
|
||||
|
||||
@@ -12,8 +12,6 @@ dotenvy = { workspace = true, optional = true }
|
||||
log = { workspace = true, optional = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"], optional = true }
|
||||
serde_json = {workspace = true, optional = true }
|
||||
tracing = {workspace = true, optional = true }
|
||||
url = { workspace = true, optional = true }
|
||||
utoipa = { workspace = true, optional = true }
|
||||
|
||||
@@ -22,7 +20,7 @@ utoipa = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
default = ["env", "network"]
|
||||
env = ["dotenvy", "log", "serde_json", "tracing"]
|
||||
env = ["dotenvy", "log"]
|
||||
network = ["schemars", "serde", "url"]
|
||||
utoipa = [ "dep:utoipa" ]
|
||||
|
||||
|
||||
@@ -51,10 +51,6 @@ pub const NYM_APIS: &[ApiUrlConst] = &[
|
||||
url: "https://nym-frontdoor.global.ssl.fastly.net/api/",
|
||||
front_hosts: Some(&["yelp.global.ssl.fastly.net"]),
|
||||
},
|
||||
ApiUrlConst {
|
||||
url: "https://cdn1.media-platform.net/api/",
|
||||
front_hosts: None,
|
||||
},
|
||||
];
|
||||
|
||||
pub const NYM_VPN_API: &str = "https://nymvpn.com/api/";
|
||||
@@ -76,13 +72,6 @@ pub const NYM_VPN_APIS: &[ApiUrlConst] = &[
|
||||
},
|
||||
];
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
fn serialize_api_urls(urls: &[ApiUrlConst]) -> String {
|
||||
serde_json::to_string(urls)
|
||||
.inspect_err(|e| tracing::warn!("failed to serialize nym_api_urls for env: {e}"))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
|
||||
pub const EXIT_POLICY_URL: &str =
|
||||
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
|
||||
@@ -173,11 +162,9 @@ pub fn export_to_env() {
|
||||
);
|
||||
set_var_to_default(var_names::NYXD, NYXD_URL);
|
||||
set_var_to_default(var_names::NYM_API, NYM_API);
|
||||
set_var_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS));
|
||||
set_var_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
||||
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
||||
set_var_to_default(var_names::NYM_VPN_API, NYM_VPN_API);
|
||||
set_var_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS));
|
||||
set_var_to_default(
|
||||
var_names::UPGRADE_MODE_ATTESTATION_URL,
|
||||
UPGRADE_MODE_ATTESTATION_URL,
|
||||
@@ -224,9 +211,6 @@ pub fn export_to_env_if_not_set() {
|
||||
);
|
||||
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
|
||||
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
|
||||
set_var_conditionally_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS));
|
||||
set_var_conditionally_to_default(var_names::NYM_VPN_API, NYM_VPN_API);
|
||||
set_var_conditionally_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS));
|
||||
set_var_conditionally_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
||||
set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
||||
set_var_conditionally_to_default(
|
||||
|
||||
@@ -7,13 +7,6 @@ use serde::{Deserialize, Serialize};
|
||||
use std::ops::Not;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
use crate::var_names;
|
||||
#[cfg(feature = "env")]
|
||||
use std::env::{VarError, var};
|
||||
#[cfg(feature = "env")]
|
||||
use std::ffi::OsStr;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ChainDetails {
|
||||
@@ -62,7 +55,7 @@ pub struct ApiUrl {
|
||||
pub front_hosts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ApiUrlConst<'a> {
|
||||
pub url: &'a str,
|
||||
pub front_hosts: Option<&'a [&'a str]>,
|
||||
@@ -113,6 +106,10 @@ impl NymNetworkDetails {
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
pub fn new_from_env() -> Self {
|
||||
use crate::var_names;
|
||||
use std::env::{VarError, var};
|
||||
use std::ffi::OsStr;
|
||||
|
||||
fn get_optional_env<K: AsRef<OsStr>>(env: K) -> Option<String> {
|
||||
match var(env) {
|
||||
Ok(var) => {
|
||||
@@ -128,11 +125,6 @@ impl NymNetworkDetails {
|
||||
}
|
||||
|
||||
let nym_api = var(var_names::NYM_API).expect("nym api not set");
|
||||
let nym_api_urls = try_parse_api_urls(var_names::NYM_APIS).unwrap_or(vec![ApiUrl {
|
||||
url: nym_api.clone(),
|
||||
front_hosts: None,
|
||||
}]);
|
||||
let nym_vpn_api_urls = try_parse_api_urls(var_names::NYM_VPN_APIS);
|
||||
|
||||
NymNetworkDetails::new_empty()
|
||||
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
|
||||
@@ -169,8 +161,10 @@ impl NymNetworkDetails {
|
||||
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
|
||||
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
|
||||
.with_nym_vpn_api_url(get_optional_env(var_names::NYM_VPN_API))
|
||||
.with_nym_vpn_api_urls(nym_vpn_api_urls)
|
||||
.with_nym_api_urls(nym_api_urls)
|
||||
.with_nym_api_urls(Some(vec![ApiUrl {
|
||||
url: nym_api,
|
||||
front_hosts: None,
|
||||
}]))
|
||||
}
|
||||
|
||||
pub fn new_mainnet() -> Self {
|
||||
@@ -224,9 +218,6 @@ impl NymNetworkDetails {
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
let nym_api_urls = self.nym_api_urls();
|
||||
let nym_vpn_api_urls = self.nym_vpn_api_urls();
|
||||
|
||||
set_var(var_names::NETWORK_NAME, self.network_name);
|
||||
set_var(var_names::BECH32_PREFIX, self.chain_details.bech32_account_prefix);
|
||||
|
||||
@@ -252,10 +243,9 @@ impl NymNetworkDetails {
|
||||
set_optional_var(var_names::COCONUT_DKG_CONTRACT_ADDRESS, self.contracts.coconut_dkg_contract_address);
|
||||
|
||||
set_optional_var(var_names::NYM_VPN_API, self.nym_vpn_api_url);
|
||||
set_optional_var(var_names::NYM_VPN_APIS, nym_vpn_api_urls);
|
||||
set_optional_var(var_names::NYM_APIS, nym_api_urls);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
pub fn default_gas_price_amount(&self) -> f64 {
|
||||
@@ -365,14 +355,8 @@ impl NymNetworkDetails {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_nym_api_urls(mut self, urls: Vec<ApiUrl>) -> Self {
|
||||
self.nym_api_urls = Some(urls);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_nym_vpn_api_urls(mut self, urls: Option<Vec<ApiUrl>>) -> Self {
|
||||
self.nym_vpn_api_urls = urls;
|
||||
pub fn with_nym_api_urls(mut self, urls: Option<Vec<ApiUrl>>) -> Self {
|
||||
self.nym_api_urls = urls;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -382,28 +366,6 @@ impl NymNetworkDetails {
|
||||
.expect("the provided nym-vpn api url is invalid!")
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
fn nym_api_urls(&self) -> Option<String> {
|
||||
serde_json::to_string(self.nym_api_urls.as_deref()?)
|
||||
.inspect_err(|e| tracing::warn!("failed to serialize nym_api_urls for env: {e}"))
|
||||
.ok()
|
||||
}
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
fn nym_vpn_api_urls(&self) -> Option<String> {
|
||||
serde_json::to_string(self.nym_vpn_api_urls.as_deref()?)
|
||||
.inspect_err(|e| tracing::warn!("failed to serialize nym_vpn_api_urls for env: {e}"))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
fn try_parse_api_urls(k: impl AsRef<OsStr>) -> Option<Vec<ApiUrl>> {
|
||||
let raw = var(k).ok()?;
|
||||
serde_json::from_str(&raw)
|
||||
.inspect_err(|e| tracing::warn!("failed to parse api urls from env \"{raw:?}\": {e}"))
|
||||
.ok()
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
|
||||
@@ -21,11 +21,9 @@ pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS";
|
||||
pub const NYXD: &str = "NYXD";
|
||||
pub const NYM_API: &str = "NYM_API";
|
||||
pub const NYM_APIS: &str = "NYM_APIS";
|
||||
pub const NYXD_WEBSOCKET: &str = "NYXD_WS";
|
||||
pub const EXIT_POLICY_URL: &str = "EXIT_POLICY";
|
||||
pub const NYM_VPN_API: &str = "NYM_VPN_API";
|
||||
pub const NYM_VPN_APIS: &str = "NYM_VPN_APIS";
|
||||
pub const CLIENT_STATS_COLLECTION_PROVIDER: &str = "CLIENT_STATS_COLLECTION_PROVIDER";
|
||||
pub const UPGRADE_MODE_ATTESTATION_URL: &str = "UPGRADE_MODE_ATTESTATION_URL";
|
||||
pub const UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY: &str = "UPGRADE_MODE_ATTESTER_ED25519_PUBKEY";
|
||||
|
||||
@@ -21,7 +21,7 @@ pub(crate) type NodeId = u32;
|
||||
macro_rules! log_err {
|
||||
($($t:tt)*) => {{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{::nym_wasm_utils::console_error!($($t)*)}
|
||||
{::wasm_utils::console_error!($($t)*)}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{::log::error!($($t)*)}
|
||||
@@ -32,7 +32,7 @@ macro_rules! log_err {
|
||||
macro_rules! log_warn {
|
||||
($($t:tt)*) => {{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{::nym_wasm_utils::console_warn!($($t)*)}
|
||||
{::wasm_utils::console_warn!($($t)*)}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{::log::warn!($($t)*)}
|
||||
@@ -43,7 +43,7 @@ macro_rules! log_warn {
|
||||
macro_rules! log_info {
|
||||
($($t:tt)*) => {{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{::nym_wasm_utils::console_log!($($t)*)}
|
||||
{::wasm_utils::console_log!($($t)*)}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{::log::info!($($t)*)}
|
||||
|
||||
@@ -7,7 +7,7 @@ use nym_sdk::mixnet::{MixnetClientSender, Recipient};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::info;
|
||||
|
||||
// Import these here for all modules to use, to keep the version consistent
|
||||
// Import these here for for all modules to use, to keep the version consistent
|
||||
pub(crate) use nym_ip_packet_requests::v8 as nym_ip_packet_requests_current;
|
||||
|
||||
mod error;
|
||||
|
||||
@@ -28,7 +28,7 @@ impl MixnetConnectionBeacon {
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_mixnet_self_ping(&mut self) -> Result<u64> {
|
||||
async fn send_mixnet_self_ping(&self) -> Result<u64> {
|
||||
trace!("Sending mixnet self ping");
|
||||
let (input_message, request_id) = create_self_ping(self.our_address);
|
||||
self.mixnet_client_sender
|
||||
@@ -38,7 +38,7 @@ impl MixnetConnectionBeacon {
|
||||
Ok(request_id)
|
||||
}
|
||||
|
||||
pub async fn run(mut self, shutdown: CancellationToken) -> Result<()> {
|
||||
pub async fn run(self, shutdown: CancellationToken) -> Result<()> {
|
||||
debug!("Mixnet connection beacon is running");
|
||||
let mut ping_interval = tokio::time::interval(MIXNET_SELF_PING_INTERVAL);
|
||||
loop {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::StreamExt;
|
||||
use nym_sdk::mixnet::{MixnetClient, MixnetMessageSender, Recipient};
|
||||
use tracing::{debug, error};
|
||||
|
||||
@@ -21,25 +22,23 @@ pub async fn self_ping_and_wait(
|
||||
wait_for_self_ping_return(mixnet_client, &request_ids).await
|
||||
}
|
||||
|
||||
async fn send_self_pings(
|
||||
our_address: Recipient,
|
||||
mixnet_client: &mut MixnetClient,
|
||||
) -> Result<Vec<u64>> {
|
||||
let sender = mixnet_client.split_sender();
|
||||
|
||||
let futures = (1..=3).map(|_| {
|
||||
let mut sender = sender.clone();
|
||||
async move {
|
||||
async fn send_self_pings(our_address: Recipient, mixnet_client: &MixnetClient) -> Result<Vec<u64>> {
|
||||
// Send pings
|
||||
let request_ids = futures::stream::iter(1..=3)
|
||||
.then(|_| async {
|
||||
let (input_message, request_id) = create_self_ping(our_address);
|
||||
sender
|
||||
mixnet_client
|
||||
.send(input_message)
|
||||
.await
|
||||
.map_err(|e| Error::NymSdkError(Box::new(e)))?;
|
||||
Ok::<_, Error>(request_id)
|
||||
}
|
||||
});
|
||||
.map_err(|err| Error::NymSdkError(Box::new(err)))?;
|
||||
Ok::<u64, Error>(request_id)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
futures::future::try_join_all(futures).await
|
||||
// Check the vec of results and return the first error, if any. If there are not errors, unwrap
|
||||
// all the results into a vec of u64s.
|
||||
request_ids.into_iter().collect::<Result<Vec<_>>>()
|
||||
}
|
||||
|
||||
async fn wait_for_self_ping_return(
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
[package]
|
||||
name = "nym-kcp"
|
||||
version = "0.1.0"
|
||||
description = "KCP protocol implementation for Nym"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[lib]
|
||||
name = "nym_kcp"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wire_format"
|
||||
path = "bin/wire_format/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "session"
|
||||
path = "bin/session/main.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
bytes = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
ansi_term = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.11"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user