Compare commits

..

57 Commits

Author SHA1 Message Date
mfahampshire f7df17e3de contracts/ experiment - publish = false for contracts 2026-01-08 13:21:49 +00:00
mfahampshire 9ddd56b2ca removal of contracts experiemnt - clippy 3 2026-01-08 13:09:46 +00:00
mfahampshire 0b552d50bb removal of contracts experiemnt - clippy 2 2026-01-08 12:52:01 +00:00
mfahampshire b143e755c4 removal of contracts/ workspace experiment - clippy 2026-01-08 12:47:29 +00:00
mfahampshire 6200899b70 removal of contracts/ workspace experiment commit 2 2026-01-08 12:35:37 +00:00
mfahampshire c82059fd06 removal of contracts/ workspace experiment commit 2026-01-08 12:35:13 +00:00
mfahampshire aea7607071 add homepage etc 2026-01-07 16:07:27 +00:00
mfahampshire d65096427b Minor comment change 2026-01-07 14:19:39 +00:00
mfahampshire 0d7e353c67 Remove todo descriptions 2026-01-07 11:12:07 +00:00
mfahampshire 778c75bed1 remove workspace version from excluded crate 2026-01-07 10:23:50 +00:00
mfahampshire 915f0c971f Add workspace repo / homepage / documentation links to cargo.toml files 2026-01-07 10:14:43 +00:00
mfahampshire 6a30a1bf8f Fix remaining path import 2026-01-07 09:56:13 +00:00
mfahampshire a999c8886d Add missing descriptions 2026-01-07 09:55:54 +00:00
mfahampshire 9e5fa65a39 Fix missing feature flags 2026-01-07 09:55:29 +00:00
mfahampshire 6b24600450 Add missing description to cargo.toml 2026-01-06 13:56:46 +00:00
mfahampshire 7621b00d53 Additional info on workspace dep comment re publish list 2026-01-06 09:36:55 +00:00
mfahampshire 162208b51c remove default features warning 2026-01-06 09:35:13 +00:00
mfahampshire 2f1f670db8 Minor comment change 2026-01-06 09:33:18 +00:00
mfahampshire 63ddadbc71 remove old published-crates file 2026-01-06 09:33:00 +00:00
mfahampshire cf1c1397db dryrun only erroring on known version problem crates 2026-01-05 18:50:02 +00:00
mfahampshire 9b34e703ec continuing with cargo.toml updates 2026-01-05 18:01:47 +00:00
mfahampshire 1802188d15 temp commit: continuing with removal of path from cargo manifest and
replacing with workspace version import for publication
2026-01-05 15:26:30 +00:00
mfahampshire efbf03a170 rename contract imports + remove package 2026-01-05 15:25:44 +00:00
mfahampshire 8faea955f5 Remove OOD info from publishing sdk guide 2026-01-05 13:40:19 +00:00
mfahampshire c0811bc2a2 Making workspace versions for publication + removing unnecessary crates
from publication
2026-01-05 13:40:04 +00:00
mfahampshire 2ab7bc6e85 rename import based on rename of contracts-common dep 2026-01-05 12:09:25 +00:00
mfahampshire b528750cdd remove script now switched to manifest based exclusion 2026-01-05 11:15:59 +00:00
mfahampshire 712ad3579c Add publish=false to manifest for cargo workspaces / crates.io
publishing exclusion
2026-01-05 11:05:09 +00:00
mfahampshire 3cf33000de mod script again 2026-01-05 09:31:43 +00:00
mfahampshire 8eb408e619 more cargo versioning - wasm utils 2026-01-05 09:31:43 +00:00
mfahampshire e2a65b07e7 more cargo versioning - wasm utils 2026-01-05 09:31:43 +00:00
mfahampshire 6e4ae07ac6 more cargo versioning 2026-01-05 09:31:43 +00:00
mfahampshire 6868c7d5ae add topology to batch 2026-01-05 09:31:43 +00:00
mfahampshire 887db8c675 chore: Release 2026-01-05 09:31:42 +00:00
mfahampshire ca3a022c62 add zeroise to feature 2026-01-05 09:31:37 +00:00
mfahampshire 99f71ff032 remove erroueously included ignore-defaults 2026-01-05 09:31:37 +00:00
mfahampshire 59d2518f63 remove uploaded sphinx-types crate from script 2026-01-05 09:31:37 +00:00
mfahampshire 8dc6ed9599 remove default features from crate, add to monorepo root dep defintion 2026-01-05 09:31:37 +00:00
mfahampshire d482c1a023 make deps workspace version 2026-01-05 09:31:37 +00:00
mfahampshire b50173c56b use workspace version 2026-01-05 09:31:37 +00:00
mfahampshire 2f70f0c9e5 use workspace version 2026-01-05 09:31:37 +00:00
mfahampshire 24d7a5692d commit to remove dirty git tree err from cargo-release for test script 2026-01-05 09:31:36 +00:00
mfahampshire 34e7f8dc09 commit to remove dirty git tree err from cargo-release 2026-01-05 09:31:35 +00:00
mfahampshire e206846d41 added more info to publishing.md 2026-01-05 09:31:26 +00:00
mfahampshire 41bbceb753 removed --no-publish flag from script 2026-01-05 09:31:26 +00:00
mfahampshire f2c0cd2861 bring non-binary / contract / tools into workspace version 2026-01-05 09:31:22 +00:00
mfahampshire acfbb93acd add publishing file 2026-01-05 09:30:35 +00:00
mfahampshire 6942cd5d2f remove nym-api from script 2026-01-05 09:30:35 +00:00
mfahampshire 809f602150 Add note to commented out docs.rs link in sdk 2026-01-05 09:30:35 +00:00
mfahampshire 05318674af Add oneliner to client_pool doc comments 2026-01-05 09:30:35 +00:00
mfahampshire 6d95fb2a39 Test remove nym-api from deps 2026-01-05 09:30:35 +00:00
mfahampshire 515b3156a6 update script 2026-01-05 09:30:35 +00:00
mfahampshire 852f2f88e7 script > release.toml 2026-01-05 09:30:35 +00:00
mfahampshire fd221a97f5 first draft release file 2026-01-05 09:30:35 +00:00
mfahampshire 837c28e6db retry versioning for cargo-release 2026-01-05 09:30:35 +00:00
mfahampshire 1eb5d62fff bring contracts into sdk global version + nym-api 2026-01-05 09:30:34 +00:00
mfahampshire 1c7ce2d903 - standardise versions for all nym-sdk workspace dependencies
- prepend sqlx-pool-guard with 'nym-'
2026-01-05 09:30:21 +00:00
643 changed files with 7694 additions and 56117 deletions
-3
View File
@@ -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
+1 -1
View File
@@ -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
+1 -9
View File
@@ -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
-47
View File
@@ -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
View File
@@ -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
View File
@@ -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])
+686
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+45 -38
View File
@@ -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
+10 -10
View File
@@ -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
+3 -4
View File
@@ -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
+14 -126
View File
@@ -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;
+5 -1
View File
@@ -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
+3 -3
View File
@@ -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 -11
View File
@@ -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
+9 -6
View File
@@ -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 dont 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
View File
@@ -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
View File
@@ -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 {
-1
View File
@@ -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;
-124
View File
@@ -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)
}
}
-19
View File
@@ -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))]
-2
View File
@@ -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"),
}
}
}
@@ -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::{
-9
View File
@@ -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 {
+2 -2
View File
@@ -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;
-8
View File
@@ -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,
],
&params,
)
.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,
+1 -1
View File
@@ -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
}
}
+2 -37
View File
@@ -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
+1 -1
View File
@@ -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"] }
+1 -16
View File
@@ -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(
+1 -4
View File
@@ -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 }
+1 -2
View File
@@ -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"]
+3 -115
View File
@@ -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);
}
}
+1 -45
View File
@@ -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()
}
-98
View File
@@ -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);
}
}
-4
View File
@@ -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;
+2 -5
View File
@@ -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 -1
View File
@@ -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;
+3 -3
View File
@@ -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(), &params._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)),
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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> =
+1 -1
View File
@@ -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};
+1 -1
View File
@@ -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};
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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};
+2 -2
View File
@@ -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]
+1 -1
View File
@@ -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
+4 -4
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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;
-24
View File
@@ -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(())
}
}
-8
View File
@@ -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()
})
}
-23
View File
@@ -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(())
}
}
+1 -1
View File
@@ -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());
+12 -15
View File
@@ -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,
+1 -3
View File
@@ -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 {
+1 -3
View File
@@ -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" ]
-16
View File
@@ -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(
+13 -51
View File
@@ -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)]
-2
View File
@@ -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";
+3 -3
View File
@@ -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)*)}
+1 -1
View File
@@ -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(
-30
View File
@@ -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