Compare commits

...

25 Commits

Author SHA1 Message Date
mfahampshire b79d4a9777 revert some changes 2025-08-18 17:56:31 +02:00
mfahampshire 9aa6332df7 modified names with nym- prefix + fixed imports 2025-07-30 22:39:56 +02:00
mfahampshire c0867cbfe3 removed sed command and made gitignore 2025-07-30 20:08:27 +02:00
mfahampshire ccdbf8dffd pushing version update squashed 2025-07-30 19:55:00 +02:00
import this 314a37cabe WG exit policy scripts update (#5921)
* add NIP-3 ports to WG manager script

* add monero ports to local testing script

* console output snippet update
2025-07-30 09:43:39 +00:00
Jack Wampler 917f391948 Make DNS Resolver fallback optional (#5920)
default to no dns system fallback, but keep support
2025-07-29 11:00:24 -06:00
Jędrzej Stuczyński a4e674c98b basic zulip client for sending messages (#5913) 2025-07-24 16:22:35 +01:00
Bogdan-Ștefan Neacşu b975d08342 Remove old free credential handle (#5864)
* Set cached storage counters to 0 (#5812)

* Set cached storage counters to 0

* u64 to i64 log possible error

* Check addition too

Debug commit

Remove more data from wg storage peer

Put actual ticket type in storage

Simplify add peer

Finish rebase

Pass defguard Peer

Cache less data for consumption

GatewayStorage traits

Wg API trait

Mock test structures

Unit test for peer controller

EcashManager trait

Init test of Authenticator

Remove peer test

* Fix windows different API

* Use make_bincode_serializer like in other places

* Add log_slow_statements to gateway storage

* Use correct LevelFilter

* Fix clippy

* More win fix

* Win clippy

* Use two error variants more

* Use only one Arc<RwLock<T>> instead of many more

* Remove commented test

* Specific trait import
2025-07-23 17:07:12 +03:00
Jędrzej Stuczyński 8e44f9f07f chore: allow compatibility with 'CDLA-Permissive-2.0' (#5910) 2025-07-23 14:48:40 +01:00
benedetta davico 8461d085a5 Merge pull request #5906 from nymtech/release/2025.13-emmental
merge release/2025.13-emmental to develop
2025-07-22 16:23:29 +02:00
Drazen Urch af9f6e5ca0 Allow PG database backend (#5880)
* feat(db): add SQL query wrapper for PostgreSQL placeholder conversion

- Created query_wrapper module with functions to automatically convert
  SQLite ? placeholders to PostgreSQL $1, $2, ... format
- Updated build.rs to handle mutually exclusive feature flags
- Modified one query in mixnodes.rs as proof of concept
- Added type conversions for PostgreSQL compatibility (u32->i64, u16->i32)

This is a checkpoint commit before converting all queries to use the wrapper.

* feat(nym-node-status-api): add PostgreSQL database support via feature flags

Implement dual database support for SQLite and PostgreSQL through Cargo feature flags.
The implementation uses a query wrapper that automatically converts SQLite-style ?
placeholders to PostgreSQL-style $1, $2, ... placeholders at runtime.

Key changes:
- Add query wrapper functions that handle placeholder conversion
- Convert all sqlx::query\! macros to use wrapper functions
- Handle type conversions between databases (i64 vs i32)
- Add feature-gated implementations for database-specific SQL syntax
- Update Makefile with clippy targets for both database features
- Document database support in README

* feat(nym-node-status-agent): add multi-API support with random selection

Agents can now connect to multiple APIs and randomly select one for each testrun:
- Accept multiple --server arguments in format "address:port:auth_key"
- Randomly shuffle server list before attempting connections
- Try each server until a testrun is obtained
- Submit results back only to the API that provided the testrun
- Continue to next server if one is down or has no testruns available

* feat(nym-node-status): implement primary/secondary server architecture

- Agent now requests testruns only from primary server (first in list)
- Results are submitted to all configured servers in parallel
- Secondary servers accept external testruns via new v2 endpoint
- Added auto-creation of gateway and testrun records on secondary servers
- New database queries: get_or_create_gateway, insert_external_testrun
- Client library enhanced with submit_results_with_context method

* Bump Node status API version

* Fix build workdir

* Bump to 3.1.4

* Fix types and queries

* 3.1.6

* Fix gateway perf, bump 3.1.7

* NodeId -> i32, 3.1.8

* Bump agent version

* i64 -> i32

* Use image yq

* Migration and more types

* Update remaining JSONB columns

* Simplify server config

* Update build path

* Change delimiter

* bump agent

* Split up pg and sqlite builds

* More typing fixes, build-and-push script

* Fix Dockerfile-pg

* Bump node-status-api

* TYping

* Agent build script

* More logging around testruns

* Fail loudly on read errors

* Cleanup

* Debug get gateways query

* Fix get_gateways query

* Use pg cert, 3.1.16

* Submit regular results to primary server

* Bump freshenss cutoff

* Update Cargo.lock

* fix: resolve rebase conflicts and compilation errors

After rebasing onto develop, fixed several issues:
- Fixed borrowed data escapes error by using sqlx::query directly in transaction functions
- Removed unused imports and cleaned up code
- Maintained database-specific implementations for transaction functions

* fmt

* Make PG default to make lives easier

* Performance improvements for Explorer v2

* Fix sqlite build

* Fix PG migration

* Tests round 1

* DB tests

* More tests

* And some more tests

* And some more, more tests

* cargo fmt

* Fix some failing lints

* Fix lioness version problems

* Clippy in tests

---------

Co-authored-by: dynco-nym <173912580+dynco-nym@users.noreply.github.com>
2025-07-22 15:25:43 +02:00
import this a9ae2017f5 [DOCs/operators]: Release notes/v2025.13 emmental & NIP-3 announcement (#5908)
* initialise PR, add dev notes and bump node version

* add operators tool and update api stats
2025-07-22 12:10:43 +00:00
Bogdan-Ștefan Neacşu 09ebe7f9e9 Support mnemonic in the NS agent (#5883)
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2025-07-22 14:21:12 +03:00
Andrej Mihajlov b72915c224 Merge pull request #5905 from nymtech/am/sqlx-guard-obtain-db-path-from-pool
sqlx-pool-guard: obtain filename from connect options
2025-07-22 11:57:55 +02:00
Andrej Mihajlov add3e864e3 sqlx-pool-guard: obtain filename from connect options 2025-07-22 11:09:39 +02:00
benedettadavico 578c9b0567 update changelog 2025-07-22 11:09:35 +02:00
Andrej Mihajlov 8f6f696f36 Merge pull request #5896 from nymtech/am/handle-table-allocate-more-memory 2025-07-22 11:09:11 +02:00
Jędrzej Stuczyński e9165763b6 Feature/dkg snapshot epoch (#5900)
* define storage item for holding historical DKG state

* make all epoch storage operations go through proxy functions

* make each saving action also apply to the historical item

* removed usage of update_epoch function

* test correct save heights

* exposed query for the epoch state at specified height

* regenerated contract schema

* restored default cw-plus behaviour as in hindsight it makes more sense
2025-07-21 17:32:57 +01:00
mfahampshire 6c1149708b GW Probe docs: Go dep. + new required mnemonic (#5897)
* add note on go dep

* updated -h and useage doc
2025-07-18 12:36:30 +00:00
Mark Sinclair aaf6931d78 nym-node-status-ui placeholder (#5902)
Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-07-17 20:04:45 +01:00
Jędrzej Stuczyński 97804f2fe5 Feature/dkg epoch dealers query (#5899)
* feat: add GetEpochDealers and GetEpochDealersAddresses queries to the DKG contract

* extended DkgQueryClient with new queries

* updated contract schema

* unit tests
2025-07-17 12:26:01 +01:00
Jędrzej Stuczyński 802d9b69ca fix: don't allow mixnode running in exit mode (#5898)
* fix: don't allow mixnode running in exit mode

* fixed error message
2025-07-17 10:57:16 +01:00
Andrej Mihajlov 7313857bc8 Allocate more memory to account for a drift in handle table size in between calls 2025-07-16 13:29:45 +02:00
benedettadavico 779174ada5 update wallet changelog 2025-07-15 14:57:49 +02:00
benedettadavico 329ad83fc0 bump versions 2025-07-15 10:04:51 +02:00
401 changed files with 16366 additions and 6772 deletions
@@ -38,10 +38,9 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
run: |
yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: cleanup-gateway-probe-ref
id: cleanup_gateway_probe_ref
+2 -3
View File
@@ -32,10 +32,9 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
run: |
yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Set GIT_TAG variable
run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
+48
View File
@@ -4,6 +4,54 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.13-emmental] (2025-07-22)
- fix: don't allow mixnode running in exit mode ([#5898])
- fix contract build process in Makefile ([#5892])
- bugfix: ignore 'Send' responses when claiming bandwidth ([#5884])
- Update push-node-status-agent.yaml ([#5882])
- listen for shutdown signals during nym-node startup ([#5879])
- feat: forbid running mixnode + entry on the same node ([#5878])
- chore: 1.88 clippy ([#5877])
- Batch SQL writes for packet stats ([#5874])
- fix the broken link ([#5873])
- Set busy_timeout in sqlx ([#5872])
- feat: basic performance contract integration [within Nym API] ([#5871])
- scraper bugfix: ignore precommits from missing validators ([#5867])
- Return true remaining ([#5866])
- Make Mix hops optional for Mixnet Client SURBs ([#5861])
- Check gateway supported versions ([#5860])
- Add build info endpoints ([#5857])
- Clear out screaming logs ([#5856])
- fix removal of qa env ([#5855])
- Use display when printing paths ([#5853])
- feat: initial performance contract ([#5833])
- Security patches for the `dkg` crate ([#5828])
- HTTP Discovery objects & network defaults ([#5814])
[#5898]: https://github.com/nymtech/nym/pull/5898
[#5892]: https://github.com/nymtech/nym/pull/5892
[#5884]: https://github.com/nymtech/nym/pull/5884
[#5882]: https://github.com/nymtech/nym/pull/5882
[#5879]: https://github.com/nymtech/nym/pull/5879
[#5878]: https://github.com/nymtech/nym/pull/5878
[#5877]: https://github.com/nymtech/nym/pull/5877
[#5874]: https://github.com/nymtech/nym/pull/5874
[#5873]: https://github.com/nymtech/nym/pull/5873
[#5872]: https://github.com/nymtech/nym/pull/5872
[#5871]: https://github.com/nymtech/nym/pull/5871
[#5867]: https://github.com/nymtech/nym/pull/5867
[#5866]: https://github.com/nymtech/nym/pull/5866
[#5861]: https://github.com/nymtech/nym/pull/5861
[#5860]: https://github.com/nymtech/nym/pull/5860
[#5857]: https://github.com/nymtech/nym/pull/5857
[#5856]: https://github.com/nymtech/nym/pull/5856
[#5855]: https://github.com/nymtech/nym/pull/5855
[#5853]: https://github.com/nymtech/nym/pull/5853
[#5833]: https://github.com/nymtech/nym/pull/5833
[#5828]: https://github.com/nymtech/nym/pull/5828
[#5814]: https://github.com/nymtech/nym/pull/5814
## [2025.12-dolcelatte] (2025-07-07)
- bugfix: key-rotation + reply SURBs ([#5876])
+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
+2055 -1988
View File
File diff suppressed because it is too large Load Diff
+15 -3
View File
@@ -39,7 +39,8 @@ members = [
"common/cosmwasm-smart-contracts/ecash-contract",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/nym-performance-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/nym-performance-contract",
"common/cosmwasm-smart-contracts/nym-pool-contract",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
@@ -100,6 +101,7 @@ members = [
"common/wasm/utils",
"common/wireguard",
"common/wireguard-types",
"common/zulip-client",
"documentation/autodoc",
"gateway",
"nym-api",
@@ -127,7 +129,7 @@ members = [
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"sqlx-pool-guard",
"nym-sqlx-pool-guard",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
@@ -167,7 +169,13 @@ default-members = [
"tools/nymvisor",
]
exclude = ["explorer", "contracts", "nym-wallet", "cpu-cycles"]
exclude = [
"explorer",
"contracts",
"nym-wallet",
"cpu-cycles",
"cargo-version-scanner",
]
[workspace.package]
authors = ["Nym Technologies SA"]
@@ -178,6 +186,8 @@ edition = "2021"
license = "Apache-2.0"
rust-version = "1.80"
readme = "README.md"
# Max: everything aside from the smart contracts and the nym-wallet use this global version, based off the fact the current highest version (aside from the contracts) is on 1.5.1
version = "1.6.0"
[workspace.dependencies]
addr = "0.15.6"
@@ -234,6 +244,7 @@ digest = "0.10.7"
dirs = "5.0"
doc-comment = "0.3"
dotenvy = "0.15.6"
dyn-clone = "1.0.19"
ecdsa = "0.16"
ed25519-dalek = "2.1"
encoding_rs = "0.8.35"
@@ -321,6 +332,7 @@ strum_macros = "0.26"
subtle-encoding = "0.5"
syn = "1"
sysinfo = "0.33.0"
tabled = "0.2.0"
tap = "1.0.1"
tar = "0.4.44"
tempfile = "3.20"
+1 -1
View File
@@ -107,7 +107,7 @@ sdk-wasm-build:
$(MAKE) -C nym-browser-extension/storage wasm-pack
$(MAKE) -C wasm/client
$(MAKE) -C wasm/node-tester
$(MAKE) -C wasm/mix-fetch
# $(MAKE) -C wasm/mix-fetch
$(MAKE) -C wasm/zknym-lib
#$(MAKE) -C wasm/full-nym-wasm
+2
View File
@@ -0,0 +1,2 @@
target/
notes.txt
+353
View File
@@ -0,0 +1,353 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "cargo-version-scanner"
version = "0.1.0"
dependencies = [
"clap",
"serde",
"toml",
]
[[package]]
name = "clap"
version = "4.5.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "indexmap"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "once_cell_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow",
]
[[package]]
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
dependencies = [
"memchr",
]
+9
View File
@@ -0,0 +1,9 @@
[package]
version = "0.1.0"
name = "cargo-version-scanner"
edition = "2021"
[dependencies]
toml = { version = "0.8.2" }
serde = { version = "1.0.219", features = ["derive"] }
clap = { version = "4.5.38", features = ["derive"] }
+18
View File
@@ -0,0 +1,18 @@
# Cargo Version Scanner
Simple tool to parse + check all versions of crates in the monorepo. Optionally outputs a nice table for quickly checking versions.
```sh
cargo run -- -h
Scan Cargo.toml files in a Rust monorepo and analyze versions
Usage: cargo-version-scanner [OPTIONS]
Options:
-v, --verbose Show verbose list of all crates, paths & versions
-u, --unset-only Only show crates with UNSET versions
--sort-by-version Sort by version instead of path (alphabetical)
-h, --help Print help
# Logs the verbose table @ the end sorted by version, instead of alphabeticly
cargo run -- -v --sort-by-version
```
+171
View File
@@ -0,0 +1,171 @@
use clap::Parser;
use std::fs;
use std::path::Path;
use std::process::Command;
struct CargoInfo {
path: String,
version: String,
directory: String,
workspace: bool,
}
#[derive(Parser)]
#[command(name = "cargo-version-scanner")]
#[command(about = "Scan Cargo.toml files in a Rust monorepo and analyze versions")]
struct Args {
/// Show verbose list of all crates, paths & versions
#[arg(short, long)]
verbose: bool,
/// Only show crates with UNSET versions
#[arg(short, long)]
unset_only: bool,
/// Sort by version instead of path (alphabetical)
#[arg(long)]
sort_by_version: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
// Get all Cargo.toml files using ripgrep
let output = Command::new("rg")
.args(&["--files", "--glob", "**/Cargo.toml"])
.current_dir("..") // Running from /nym/cargo-version-scanner/ so have to go on dir up
.output()?;
if !output.status.success() {
eprintln!(
"Failed to run ripgrep: {}",
String::from_utf8_lossy(&output.stderr)
);
std::process::exit(1);
}
let files_output = String::from_utf8(output.stdout)?;
let mut cargo_infos = Vec::new();
println!("Found {} files", files_output.lines().count());
let mut cargo_files: Vec<&str> = files_output.lines().collect();
cargo_files.sort();
for file_path in cargo_files {
if file_path == "Cargo.toml" || file_path.starts_with("cargo-version-scanner/") {
continue;
}
let dir_name = Path::new(file_path)
.parent()
.and_then(|p| p.file_name())
.and_then(|n| n.to_str())
.unwrap_or("unknown");
let (version, workspace) = match get_version_from_cargo_toml(file_path) {
Ok((Some(v), is_workspace)) => (v, is_workspace),
Ok((None, is_workspace)) => ("UNSET".to_string(), is_workspace),
Err(_) => ("ERROR".to_string(), false),
};
cargo_infos.push(CargoInfo {
path: file_path.to_string(),
version,
directory: dir_name.to_string(),
workspace,
});
}
if args.sort_by_version {
cargo_infos.sort_by(|a, b| a.version.cmp(&b.version));
} else {
cargo_infos.sort_by(|a, b| a.path.cmp(&b.path));
}
let mut version_counts = std::collections::HashMap::new();
let mut unset_infos = Vec::new();
let mut workspace_count = 0;
for info in &cargo_infos {
*version_counts.entry(&info.version).or_insert(0) += 1;
if info.version == "UNSET" {
unset_infos.push(info);
}
if info.workspace {
workspace_count += 1;
}
}
if args.unset_only {
println!("\nDirectories with UNSET versions ({}):", unset_infos.len());
for info in &unset_infos {
println!("{} {}", info.directory, info.path);
}
} else {
println!("Version distribution:");
let mut sorted_versions: Vec<_> = version_counts.iter().collect();
sorted_versions.sort_by_key(|(version, _)| *version);
for (version, count) in sorted_versions {
println!("{}: {}", version, count);
}
if !unset_infos.is_empty() {
println!("\nDirectories with UNSET versions ({}):", unset_infos.len());
for info in &unset_infos {
println!("{} {}", info.directory, info.path);
}
}
println!("\nTotal crates: {}", cargo_infos.len());
println!("Workspace inherited: {}", workspace_count);
}
if args.verbose {
let sort_desc = if args.sort_by_version {
"version"
} else {
"path"
};
println!("\nAll crates (sorted by {}):", sort_desc);
for info in &cargo_infos {
let workspace_indicator = if info.workspace {
"workspace"
} else {
"explicit"
};
println!(
"{:<20} {:<15} {:<10} {}",
info.directory, info.version, workspace_indicator, info.path
);
}
}
Ok(())
}
fn get_version_from_cargo_toml(
file_path: &str,
) -> Result<(Option<String>, bool), Box<dyn std::error::Error>> {
let full_path = format!("../{}", file_path); // Also have to go one dir up for parsing
let content = fs::read_to_string(&full_path)?;
let toml_value: toml::Value = toml::from_str(&content)?;
// Look for version in [package] section
if let Some(package) = toml_value.get("package") {
if let Some(version) = package.get("version") {
if let Some(version_str) = version.as_str() {
return Ok((Some(version_str.to_string()), false));
} else if let Some(version_table) = version.as_table() {
// Check if it's { workspace = true }
if let Some(workspace) = version_table.get("workspace") {
if workspace.as_bool() == Some(true) {
return Ok((Some("workspace".to_string()), true));
}
}
}
}
}
Ok((None, false))
}
+5 -2
View File
@@ -1,7 +1,10 @@
[package]
name = "nym-client"
version = "1.1.58"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
version = "1.1.59"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
]
description = "Implementation of the Nym Client"
edition = "2021"
rust-version = "1.70"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client-websocket-requests"
version = "0.1.0"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.58"
version = "1.1.59"
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"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-async-file-watcher"
version = "0.1.0"
version.workspace = true
edition.workspace = true
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-authenticator-requests"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -28,8 +28,6 @@ pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64;
pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
pub ipv4: Ipv4Addr,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-bandwidth-controller"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-bin-common"
version = "0.6.0"
version.workspace = true
description = "Common code for nym binaries"
edition = { workspace = true }
authors = { workspace = true }
+5 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client-core"
version = "1.1.15"
version.workspace = true
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.76"
@@ -100,11 +100,11 @@ workspace = true
workspace = true
features = ["tokio"]
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.3.0"
features = ["futures"]
# [target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
# version.workspace = true
# features = ["futures"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
[target."cfg(target_arch = \"wasm32\")".dependencies.nym-wasm-utils]
path = "../wasm/utils"
features = ["websocket"]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client-core-config-types"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-client-core-gateways-storage"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client-core-surb-storage"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
@@ -26,8 +26,8 @@ workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"]
optional = true
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx-pool-guard]
path = "../../../sqlx-pool-guard"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-sqlx-pool-guard]
path = "../../../nym-sqlx-pool-guard"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
@@ -13,7 +13,7 @@ use std::path::Path;
use time::OffsetDateTime;
use tracing::{error, info};
use sqlx_pool_guard::SqlitePoolGuard;
use nym_sqlx_pool_guard::SqlitePoolGuard;
#[derive(Debug, Clone)]
pub struct StorageManager {
@@ -37,7 +37,7 @@ impl StorageManager {
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(&database_path)
.filename(database_path)
.create_if_missing(fresh)
.disable_statement_logging();
@@ -49,8 +49,7 @@ impl StorageManager {
}
};
let connection_pool =
SqlitePoolGuard::new(database_path.as_ref().to_path_buf(), connection_pool);
let connection_pool = SqlitePoolGuard::new(connection_pool);
if let Err(err) = sqlx::migrate!("./fs_surbs_migrations")
.run(&*connection_pool)
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-gateway-client"
version = "0.1.0"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
@@ -61,7 +61,7 @@ workspace = true
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
workspace = true
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
[target."cfg(target_arch = \"wasm32\")".dependencies.nym-wasm-utils]
path = "../../wasm/utils"
features = ["websocket"]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnet-client"
version = "0.1.0"
version.workspace = true
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-validator-client"
version = "0.1.0"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.56"
@@ -41,6 +41,11 @@ pub trait DkgQueryClient {
self.query_dkg_contract(request).await
}
async fn get_epoch_at_height(&self, height: u64) -> Result<Option<Epoch>, NyxdError> {
let request = DkgQueryMsg::GetEpochStateAtHeight { height };
self.query_dkg_contract(request).await
}
async fn can_advance_state(&self) -> Result<StateAdvanceResponse, NyxdError> {
let request = DkgQueryMsg::CanAdvanceState {};
self.query_dkg_contract(request).await
@@ -87,6 +92,34 @@ pub trait DkgQueryClient {
self.query_dkg_contract(request).await
}
async fn get_epoch_dealers_paged(
&self,
epoch_id: EpochId,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedDealerResponse, NyxdError> {
let request = DkgQueryMsg::GetEpochDealers {
epoch_id,
start_after,
limit,
};
self.query_dkg_contract(request).await
}
async fn get_epoch_dealers_addresses_paged(
&self,
epoch_id: EpochId,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedDealerResponse, NyxdError> {
let request = DkgQueryMsg::GetEpochDealersAddresses {
epoch_id,
start_after,
limit,
};
self.query_dkg_contract(request).await
}
async fn get_dealer_indices_paged(
&self,
start_after: Option<String>,
@@ -208,6 +241,20 @@ pub trait PagedDkgQueryClient: DkgQueryClient {
collect_paged!(self, get_current_dealers_paged, dealers)
}
async fn get_all_epoch_dealers(
&self,
epoch_id: EpochId,
) -> Result<Vec<DealerDetails>, NyxdError> {
collect_paged!(self, get_epoch_dealers_paged, dealers, epoch_id)
}
async fn get_all_epoch_dealers_addresses(
&self,
epoch_id: EpochId,
) -> Result<Vec<DealerDetails>, NyxdError> {
collect_paged!(self, get_epoch_dealers_addresses_paged, dealers, epoch_id)
}
async fn get_all_dealer_indices(&self) -> Result<Vec<(Addr, NodeIndex)>, NyxdError> {
collect_paged!(self, get_dealer_indices_paged, indices)
}
@@ -257,6 +304,9 @@ mod tests {
match msg {
DkgQueryMsg::GetState {} => client.get_state().ignore(),
DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(),
DkgQueryMsg::GetEpochStateAtHeight { height } => {
client.get_epoch_at_height(height).ignore()
}
DkgQueryMsg::CanAdvanceState {} => client.can_advance_state().ignore(),
DkgQueryMsg::GetCurrentEpochThreshold {} => {
client.get_current_epoch_threshold().ignore()
@@ -276,6 +326,20 @@ mod tests {
DkgQueryMsg::GetCurrentDealers { limit, start_after } => client
.get_current_dealers_paged(start_after, limit)
.ignore(),
QueryMsg::GetEpochDealers {
epoch_id,
limit,
start_after,
} => client
.get_epoch_dealers_paged(epoch_id, start_after, limit)
.ignore(),
QueryMsg::GetEpochDealersAddresses {
epoch_id,
limit,
start_after,
} => client
.get_epoch_dealers_addresses_paged(epoch_id, start_after, limit)
.ignore(),
DkgQueryMsg::GetDealerIndices { limit, start_after } => {
client.get_dealer_indices_paged(start_after, limit).ignore()
}
+5 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli-commands"
version = "1.0.0"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
@@ -38,7 +38,9 @@ cosmrs = { workspace = true }
cosmwasm-std = { workspace = true }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
nym-bin-common = { path = "../../common/bin-common", features = [
"output_format",
] }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" }
@@ -59,5 +61,5 @@ nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-credential-proxy-requests = { path = "../../nym-credential-proxy/nym-credential-proxy-requests" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-pemstore = { path = "../../common/pemstore" }
nym-types = { path = "../../common/types" }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-config"
version = "0.1.0"
version.workspace = true
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-coconut-dkg-common"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
@@ -55,6 +55,14 @@ impl DealerDetailsResponse {
}
}
#[cw_serde]
pub struct PagedDealerAddressesResponse {
pub dealers: Vec<Addr>,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<Addr>,
}
#[cw_serde]
pub struct PagedDealerResponse {
pub dealers: Vec<DealerDetails>,
@@ -12,8 +12,8 @@ use cosmwasm_schema::cw_serde;
#[cfg(feature = "schema")]
use crate::{
dealer::{
DealerDetailsResponse, PagedDealerIndexResponse, PagedDealerResponse,
RegisteredDealerDetails,
DealerDetailsResponse, PagedDealerAddressesResponse, PagedDealerIndexResponse,
PagedDealerResponse, RegisteredDealerDetails,
},
dealing::{
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
@@ -84,6 +84,9 @@ pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(Epoch))]
GetCurrentEpochState {},
#[cfg_attr(feature = "schema", returns(Option<Epoch>))]
GetEpochStateAtHeight { height: u64 },
#[cfg_attr(feature = "schema", returns(u64))]
GetCurrentEpochThreshold {},
@@ -102,6 +105,20 @@ pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(DealerDetailsResponse))]
GetDealerDetails { dealer_address: String },
#[cfg_attr(feature = "schema", returns(PagedDealerAddressesResponse))]
GetEpochDealersAddresses {
epoch_id: EpochId,
limit: Option<u32>,
start_after: Option<String>,
},
#[cfg_attr(feature = "schema", returns(PagedDealerResponse))]
GetEpochDealers {
epoch_id: EpochId,
limit: Option<u32>,
start_after: Option<String>,
},
#[cfg_attr(feature = "schema", returns(PagedDealerResponse))]
GetCurrentDealers {
limit: Option<u32>,
@@ -1,6 +1,6 @@
[package]
name = "nym-contracts-common-testing"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-contracts-common"
version = "0.5.0"
version.workspace = true
description = "Common library for Nym cosmwasm contracts"
edition = { workspace = true }
authors = { workspace = true }
@@ -1,6 +1,6 @@
[package]
name = "easy-addr"
version = "0.1.0"
name = "nym-easy-addr"
version.workspace = true
edition = "2021"
publish = false
license.workspace = true
@@ -11,4 +11,4 @@ proc-macro = true
[dependencies]
cosmwasm-std = { workspace = true }
quote = { workspace = true }
syn = { workspace = true, features = ["full", "printing", "extra-traits"] }
syn = { workspace = true, features = ["full", "printing", "extra-traits"] }
@@ -1,6 +1,6 @@
[package]
name = "nym-ecash-contract-common"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnet-contract-common"
version = "0.6.0"
version.workspace = true
description = "Common library for the Nym mixnet contract"
rust-version = "1.62"
edition = { workspace = true }
@@ -22,7 +22,7 @@ semver = { workspace = true, features = ["serde"] }
# we still have to preserve that import for `JsonSchema` for `Layer` type (since we can't use cw_serde macro due to custom serde impl)
schemars = { workspace = true }
thiserror = { workspace = true }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
humantime-serde = { workspace = true }
utoipa = { workspace = true, optional = true }
@@ -41,4 +41,4 @@ schema = ["cw2"]
generate-ts = ['ts-rs']
[lints]
workspace = true
workspace = true
@@ -0,0 +1,12 @@
[package]
name = "nym-group-contract-common"
version.workspace = true
edition = "2021"
license.workspace = true
[dependencies]
cosmwasm-schema = { workspace = true }
cw-controllers = { workspace = true }
cw4 = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true, features = ["derive"] }
@@ -0,0 +1 @@
pub mod msg;
@@ -0,0 +1,50 @@
use cosmwasm_schema::{cw_serde, QueryResponses};
use cw4::Member;
#[cw_serde]
pub struct InstantiateMsg {
/// The admin is the only account that can update the group state.
/// Omit it to make the group immutable.
pub admin: Option<String>,
pub members: Vec<Member>,
}
#[cw_serde]
pub enum ExecuteMsg {
/// Change the admin
UpdateAdmin { admin: Option<String> },
/// apply a diff to the existing members.
/// remove is applied after add, so if an address is in both, it is removed
UpdateMembers {
remove: Vec<String>,
add: Vec<Member>,
},
/// Add a new hook to be informed of all membership changes. Must be called by Admin
AddHook { addr: String },
/// Remove a hook. Must be called by Admin
RemoveHook { addr: String },
}
#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
#[returns(cw_controllers::AdminResponse)]
Admin {},
#[returns(cw4::TotalWeightResponse)]
TotalWeight { at_height: Option<u64> },
#[returns(cw4::MemberListResponse)]
ListMembers {
start_after: Option<String>,
limit: Option<u32>,
},
#[returns(cw4::MemberResponse)]
Member {
addr: String,
at_height: Option<u64>,
},
/// Shows all registered hooks.
#[returns(cw_controllers::HooksResponse)]
Hooks {},
}
#[cw_serde]
pub struct MigrateMsg {}
@@ -1,6 +1,6 @@
[package]
name = "nym-multisig-contract-common"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-performance-contract-common"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-pool-contract-common"
version = "0.1.0"
version.workspace = true
description = "Common library for the Nym Pool contract"
authors.workspace = true
repository.workspace = true
@@ -1,6 +1,6 @@
[package]
name = "nym-vesting-contract-common"
version = "0.7.0"
version.workspace = true
description = "Common library for the Nym vesting contract"
edition = { workspace = true }
authors = { workspace = true }
@@ -11,8 +11,8 @@ repository = { workspace = true }
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw2 = { workspace = true, optional = true }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
# without this feature, cargo clippy emits a ton of incompatibility warnings
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-credential-storage"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
@@ -20,8 +20,8 @@ nym-credentials = { path = "../credentials" }
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
nym-ecash-time = { path = "../ecash-time" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx-pool-guard]
path = "../../sqlx-pool-guard"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-sqlx-pool-guard]
path = "../../nym-sqlx-pool-guard"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
@@ -6,8 +6,8 @@ use crate::models::{
RawVerificationKey, StoredIssuedTicketbook, StoredPendingTicketbook,
};
use nym_ecash_time::Date;
use nym_sqlx_pool_guard::SqlitePoolGuard;
use sqlx::{Executor, Sqlite, Transaction};
use sqlx_pool_guard::SqlitePoolGuard;
#[derive(Clone)]
pub struct SqliteEcashTicketbookManager {
@@ -33,11 +33,11 @@ use nym_credentials::{
IssuanceTicketBook, IssuedTicketBook,
};
use nym_ecash_time::{ecash_today, Date, EcashTime};
use nym_sqlx_pool_guard::SqlitePoolGuard;
use sqlx::{
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
ConnectOptions,
};
use sqlx_pool_guard::SqlitePoolGuard;
use std::path::Path;
use zeroize::Zeroizing;
@@ -63,7 +63,7 @@ impl PersistentStorage {
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(&database_path)
.filename(database_path)
.create_if_missing(true)
.disable_statement_logging();
@@ -75,8 +75,7 @@ impl PersistentStorage {
}
};
let connection_pool =
SqlitePoolGuard::new(database_path.as_ref().to_path_buf(), connection_pool);
let connection_pool = SqlitePoolGuard::new(connection_pool);
if let Err(err) = sqlx::migrate!("./migrations").run(&*connection_pool).await {
error!("Failed to perform migration on the SQLx database: {err}");
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-credential-utils"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
+3 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-credential-verification"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -11,9 +11,11 @@ rust-version.workspace = true
readme.workspace = true
[dependencies]
async-trait = { workspace = true }
bs58 = { workspace = true }
cosmwasm-std = { workspace = true }
cw-utils = { workspace = true }
dyn-clone = { workspace = true }
futures = { workspace = true }
rand = { workspace = true }
si-scale = { workspace = true }
@@ -7,25 +7,36 @@ use crate::ClientBandwidth;
use nym_credentials::ecash::utils::ecash_today;
use nym_credentials_interface::Bandwidth;
use nym_gateway_requests::ServerResponse;
use nym_gateway_storage::GatewayStorage;
use nym_gateway_storage::traits::BandwidthGatewayStorage;
use si_scale::helpers::bibytes2;
use time::OffsetDateTime;
use tracing::*;
const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB
#[derive(Clone)]
pub struct BandwidthStorageManager {
pub(crate) storage: GatewayStorage,
pub(crate) storage: Box<dyn BandwidthGatewayStorage + Send + Sync>,
pub(crate) client_bandwidth: ClientBandwidth,
pub(crate) client_id: i64,
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
pub(crate) only_coconut_credentials: bool,
}
impl Clone for BandwidthStorageManager {
fn clone(&self) -> Self {
Self {
storage: dyn_clone::clone_box(&*self.storage),
client_bandwidth: self.client_bandwidth.clone(),
client_id: self.client_id,
bandwidth_cfg: self.bandwidth_cfg,
only_coconut_credentials: self.only_coconut_credentials,
}
}
}
impl BandwidthStorageManager {
pub fn new(
storage: GatewayStorage,
storage: Box<dyn BandwidthGatewayStorage + Send + Sync>,
client_bandwidth: ClientBandwidth,
client_id: i64,
bandwidth_cfg: BandwidthFlushingBehaviourConfig,
+123 -35
View File
@@ -2,12 +2,14 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::Error;
use async_trait::async_trait;
use credential_sender::CredentialHandler;
use credential_sender::CredentialHandlerConfig;
use error::EcashTicketError;
use futures::channel::mpsc::{self, UnboundedSender};
use nym_credentials::CredentialSpendingData;
use nym_credentials_interface::{ClientTicket, CompactEcashError, NymPayInfo, VerificationKeyAuth};
use nym_gateway_storage::traits::BandwidthGatewayStorage;
use nym_gateway_storage::GatewayStorage;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::DirectSigningHttpRpcNyxdClient;
@@ -20,6 +22,7 @@ pub mod credential_sender;
pub mod error;
mod helpers;
mod state;
pub mod traits;
pub const TIME_RANGE_SEC: i64 = 30;
@@ -31,44 +34,21 @@ pub struct EcashManager {
cred_sender: UnboundedSender<ClientTicket>,
}
impl EcashManager {
pub async fn new(
credential_handler_cfg: CredentialHandlerConfig,
nyxd_client: DirectSigningHttpRpcNyxdClient,
pk_bytes: [u8; 32],
shutdown: nym_task::TaskClient,
storage: GatewayStorage,
) -> Result<Self, Error> {
let shared_state = SharedState::new(nyxd_client, storage).await?;
let (cred_sender, cred_receiver) = mpsc::unbounded();
let cs =
CredentialHandler::new(credential_handler_cfg, cred_receiver, shared_state.clone())
.await?;
cs.start(shutdown);
Ok(EcashManager {
shared_state,
pk_bytes,
pay_infos: Default::default(),
cred_sender,
})
}
pub async fn verification_key(
#[async_trait]
impl traits::EcashManager for EcashManager {
async fn verification_key(
&self,
epoch_id: EpochId,
) -> Result<RwLockReadGuard<VerificationKeyAuth>, EcashTicketError> {
self.shared_state.verification_key(epoch_id).await
}
pub fn storage(&self) -> &GatewayStorage {
&self.shared_state.storage
fn storage(&self) -> Box<dyn BandwidthGatewayStorage + Send + Sync> {
dyn_clone::clone_box(&*self.shared_state.storage)
}
//Check for duplicate pay_info, then check the payment, then insert pay_info if everything succeeded
pub async fn check_payment(
async fn check_payment(
&self,
credential: &CredentialSpendingData,
aggregated_verification_key: &VerificationKeyAuth,
@@ -88,6 +68,40 @@ impl EcashManager {
.await
}
fn async_verify(&self, ticket: ClientTicket) {
// TODO: I guess do something for shutdowns
let _ = self
.cred_sender
.unbounded_send(ticket)
.inspect_err(|_| error!("failed to send the client ticket for verification task"));
}
}
impl EcashManager {
pub async fn new(
credential_handler_cfg: CredentialHandlerConfig,
nyxd_client: DirectSigningHttpRpcNyxdClient,
pk_bytes: [u8; 32],
shutdown: nym_task::TaskClient,
storage: GatewayStorage,
) -> Result<Self, Error> {
let shared_state = SharedState::new(nyxd_client, Box::new(storage)).await?;
let (cred_sender, cred_receiver) = mpsc::unbounded();
let cs =
CredentialHandler::new(credential_handler_cfg, cred_receiver, shared_state.clone())
.await?;
cs.start(shutdown);
Ok(EcashManager {
shared_state,
pk_bytes,
pay_infos: Default::default(),
cred_sender,
})
}
pub async fn verify_pay_info(&self, pay_info: NymPayInfo) -> Result<usize, EcashTicketError> {
//Public key check
if pay_info.pk() != self.pk_bytes {
@@ -152,12 +166,86 @@ impl EcashManager {
inner.insert(index, pay_info);
Ok(())
}
}
pub fn async_verify(&self, ticket: ClientTicket) {
// TODO: I guess do something for shutdowns
let _ = self
.cred_sender
.unbounded_send(ticket)
.inspect_err(|_| error!("failed to send the client ticket for verification task"));
pub struct MockEcashManager {
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 {
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,
155, 18, 219, 195, 182, 3, 56, 168, 16, 93, 154, 249, 230, 16, 202, 90, 134,
246, 25, 98, 6, 175, 215, 188, 239, 71, 84, 66, 1, 43, 66, 197, 180, 216, 80,
55, 185, 140, 216, 14, 48, 244, 214, 20, 68, 106, 41, 48, 252, 188, 181, 231,
170, 23, 211, 215, 12, 91, 147, 47, 7, 4, 0, 0, 0, 0, 0, 0, 0, 174, 31, 237,
215, 159, 183, 71, 125, 90, 147, 84, 78, 49, 216, 66, 232, 92, 206, 41, 230,
239, 209, 211, 166, 131, 190, 148, 36, 225, 194, 146, 6, 120, 34, 194, 5, 154,
155, 234, 41, 191, 119, 227, 51, 91, 128, 151, 240, 129, 208, 253, 171, 234,
170, 71, 139, 251, 78, 49, 35, 218, 16, 77, 150, 177, 204, 83, 210, 67, 147,
66, 162, 58, 25, 96, 168, 61, 180, 92, 21, 18, 78, 194, 98, 176, 123, 122, 176,
81, 150, 187, 20, 64, 69, 0, 134, 142, 3, 84, 108, 3, 55, 107, 111, 73, 31, 46,
51, 225, 248, 202, 173, 194, 24, 104, 96, 31, 61, 24, 140, 220, 31, 176, 200,
30, 217, 66, 58, 11, 181, 158, 196, 179, 199, 177, 7, 210, 4, 119, 142, 149,
59, 3, 186, 145, 27, 230, 125, 230, 246, 197, 196, 119, 70, 239, 115, 99, 215,
63, 205, 63, 74, 108, 201, 42, 226, 150, 137, 3, 157, 45, 25, 163, 54, 107,
153, 61, 141, 64, 207, 139, 41, 203, 39, 36, 97, 181, 72, 206, 235, 221, 178,
171, 60, 4, 6, 170, 181, 213, 10, 216, 53, 28, 32, 33, 41, 224, 60, 247, 206,
137, 108, 251, 229, 234, 112, 65, 145, 124, 212, 125, 116, 154, 114, 2, 125,
202, 24, 25, 196, 219, 104, 200, 131, 133, 180, 39, 21, 144, 204, 8, 151, 218,
99, 64, 209, 47, 5, 42, 13, 214, 139, 54, 112, 224, 53, 238, 250, 56, 42, 105,
15, 21, 238, 99, 225, 79, 121, 104, 155, 230, 243, 133, 47, 39, 147, 98, 45,
113, 137, 200, 102, 151, 122, 174, 9, 250, 17, 138, 191, 129, 202, 244, 107,
75, 48, 141, 136, 89, 168, 124, 88, 174, 251, 17, 35, 146, 88, 76, 134, 102,
105, 204, 16, 176, 214, 63, 13, 170, 225, 250, 112, 7, 237, 161, 160, 15, 71,
10, 130, 137, 69, 186, 64, 223, 188, 5, 5, 228, 57, 214, 134, 247, 20, 171,
140, 43, 230, 57, 29, 127, 136, 169, 80, 14, 137, 130, 200, 205, 222, 81, 143,
40, 77, 68, 197, 91, 142, 91, 84, 164, 15, 133, 242, 149, 255, 173, 201, 108,
208, 23, 188, 230, 158, 146, 54, 198, 52, 148, 123, 202, 52, 222, 50, 4, 62,
211, 208, 176, 61, 104, 151, 227, 192, 224, 200, 132, 53, 187, 240, 254, 150,
60, 30, 140, 11, 63, 71, 12, 30, 233, 255, 144, 250, 16, 81, 38, 33, 9, 185,
195, 214, 0, 119, 117, 94, 100, 103, 144, 10, 189, 65, 113, 114, 192, 11, 177,
214, 223, 218, 36, 139, 183, 2, 206, 247, 245, 88, 62, 231, 183, 50, 46, 95,
202, 152, 82, 244, 80, 173, 192, 147, 51, 248, 46, 181, 194, 205, 233, 67, 144,
155, 250, 142, 124, 71, 9, 136, 142, 88, 29, 99, 222, 43, 181, 172, 120, 187,
179, 172, 240, 231, 57, 236, 195, 158, 182, 203, 19, 49, 220, 180, 212, 101,
105, 239, 58, 215, 0, 50, 100, 172, 29, 236, 170, 108, 129, 150, 5, 64, 238,
59, 50, 4, 21, 131, 197, 142, 191, 76, 101, 140, 133, 112, 38, 235, 113, 203,
22, 161, 204, 84, 73, 125, 219, 70, 62, 67, 119, 52, 130, 208, 180, 231, 78,
141, 181, 13, 207, 196, 126, 159, 70, 34, 195, 70,
])
.unwrap(),
),
storage: dyn_clone::clone_box(&*storage),
}
}
}
#[async_trait]
impl traits::EcashManager for MockEcashManager {
async fn verification_key(
&self,
_epoch_id: EpochId,
) -> Result<RwLockReadGuard<VerificationKeyAuth>, EcashTicketError> {
Ok(self.verfication_key.read().await)
}
fn storage(&self) -> Box<dyn BandwidthGatewayStorage + Send + Sync> {
dyn_clone::clone_box(&*self.storage)
}
async fn check_payment(
&self,
_credential: &CredentialSpendingData,
_aggregated_verification_key: &VerificationKeyAuth,
) -> Result<(), EcashTicketError> {
Ok(())
}
fn async_verify(&self, _ticket: ClientTicket) {}
}
@@ -6,7 +6,7 @@ use crate::Error;
use cosmwasm_std::{from_json, CosmosMsg, WasmMsg};
use nym_credentials_interface::VerificationKeyAuth;
use nym_ecash_contract_common::msg::ExecuteMsg;
use nym_gateway_storage::GatewayStorage;
use nym_gateway_storage::traits::BandwidthGatewayStorage;
use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{
@@ -22,18 +22,28 @@ use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use tracing::{error, trace, warn};
// state shared by different subtasks dealing with credentials
#[derive(Clone)]
pub(crate) struct SharedState {
pub(crate) nyxd_client: Arc<RwLock<DirectSigningHttpRpcNyxdClient>>,
pub(crate) address: AccountId,
pub(crate) epoch_data: Arc<RwLock<BTreeMap<EpochId, EpochState>>>,
pub(crate) storage: GatewayStorage,
pub(crate) storage: Box<dyn BandwidthGatewayStorage + Send + Sync>,
}
impl Clone for SharedState {
fn clone(&self) -> Self {
Self {
nyxd_client: self.nyxd_client.clone(),
address: self.address.clone(),
epoch_data: self.epoch_data.clone(),
storage: dyn_clone::clone_box(&*self.storage),
}
}
}
impl SharedState {
pub(crate) async fn new(
nyxd_client: DirectSigningHttpRpcNyxdClient,
storage: GatewayStorage,
storage: Box<dyn BandwidthGatewayStorage + Send + Sync>,
) -> Result<Self, Error> {
let address = nyxd_client.address();
@@ -0,0 +1,23 @@
use async_trait::async_trait;
use nym_credentials::CredentialSpendingData;
use nym_credentials_interface::{ClientTicket, VerificationKeyAuth};
use nym_gateway_storage::traits::BandwidthGatewayStorage;
use nym_validator_client::nym_api::EpochId;
use tokio::sync::RwLockReadGuard;
use crate::ecash::error::EcashTicketError;
#[async_trait]
pub trait EcashManager {
async fn verification_key(
&self,
epoch_id: EpochId,
) -> Result<RwLockReadGuard<VerificationKeyAuth>, EcashTicketError>;
fn storage(&self) -> Box<dyn BandwidthGatewayStorage + Send + Sync>;
async fn check_payment(
&self,
credential: &CredentialSpendingData,
aggregated_verification_key: &VerificationKeyAuth,
) -> Result<(), EcashTicketError>;
fn async_verify(&self, ticket: ClientTicket);
}
+3 -3
View File
@@ -1,8 +1,8 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ecash::traits::EcashManager;
use bandwidth_storage_manager::BandwidthStorageManager;
use ecash::EcashManager;
use nym_credentials::ecash::utils::{cred_exp_date, ecash_today, EcashTime};
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
use nym_gateway_requests::models::CredentialSpendingRequest;
@@ -20,14 +20,14 @@ pub mod error;
pub struct CredentialVerifier {
credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager>,
ecash_verifier: Arc<dyn EcashManager + Send + Sync>,
bandwidth_storage_manager: BandwidthStorageManager,
}
impl CredentialVerifier {
pub fn new(
credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager>,
ecash_verifier: Arc<dyn EcashManager + Send + Sync>,
bandwidth_storage_manager: BandwidthStorageManager,
) -> Self {
CredentialVerifier {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-credentials-interface"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-credentials"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
+13 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-crypto"
version = "0.4.0"
version.workspace = true
description = "Crypto library for the nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
@@ -19,7 +19,9 @@ generic-array = { workspace = true, optional = true }
hkdf = { workspace = true, optional = true }
hmac = { workspace = true, optional = true }
cipher = { workspace = true, optional = true }
x25519-dalek = { workspace = true, features = ["static_secrets"], optional = true }
x25519-dalek = { workspace = true, features = [
"static_secrets",
], optional = true }
ed25519-dalek = { workspace = true, features = ["rand_core"], optional = true }
rand = { workspace = true, optional = true }
serde_bytes = { workspace = true, optional = true }
@@ -30,8 +32,8 @@ thiserror = { workspace = true }
zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
# internal
nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0", default-features = false }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-sphinx-types = { path = "../nymsphinx/types", default-features = false }
nym-pemstore = { path = "../../common/pemstore" }
[dev-dependencies]
rand_chacha = { workspace = true }
@@ -39,11 +41,16 @@ rand_chacha = { workspace = true }
[features]
default = []
aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"]
serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"]
serde = [
"dep:serde",
"serde_bytes",
"ed25519-dalek/serde",
"x25519-dalek/serde",
]
asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"]
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array", "sha2"]
stream_cipher = ["aes", "ctr", "cipher", "generic-array"]
sphinx = ["nym-sphinx-types/sphinx"]
[lints]
workspace = true
workspace = true
+7 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-dkg"
version = "0.1.0"
version.workspace = true
edition = "2021"
resolver = "2"
license.workspace = true
@@ -12,7 +12,12 @@ bitvec = { workspace = true }
# 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
bls12_381 = { workspace = true, default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
bls12_381 = { workspace = true, default-features = false, features = [
"alloc",
"pairings",
"experimental",
"zeroize",
] }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common", optional = true }
bs58 = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-ecash-time"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-execute"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-exit-policy"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway-requests"
version = "0.1.0"
version.workspace = true
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-gateway-stats-storage"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+8 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-gateway-storage"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -9,8 +9,10 @@ edition.workspace = true
license.workspace = true
[dependencies]
async-trait = { workspace = true }
bincode = { workspace = true }
defguard_wireguard_rs = { workspace = true }
dyn-clone = { workspace = true }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
@@ -21,6 +23,7 @@ sqlx = { workspace = true, features = [
] }
time = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["sync"], optional = true }
tracing = { workspace = true }
nym-credentials-interface = { path = "../credentials-interface" }
@@ -35,3 +38,7 @@ sqlx = { workspace = true, features = [
"macros",
"migrate",
] }
[features]
default = []
mock = ["tokio"]
@@ -0,0 +1,19 @@
/*
* Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
DELETE FROM wireguard_peer WHERE client_id IS NULL;
CREATE TABLE wireguard_peer_new
(
public_key TEXT NOT NULL PRIMARY KEY UNIQUE,
allowed_ips BLOB NOT NULL,
client_id INTEGER REFERENCES clients(id) NOT NULL
);
INSERT INTO wireguard_peer_new (public_key, allowed_ips, client_id)
SELECT public_key, allowed_ips, client_id FROM wireguard_peer;
DROP TABLE wireguard_peer;
ALTER TABLE wireguard_peer_new RENAME TO wireguard_peer;
+13
View File
@@ -3,6 +3,8 @@
use std::str::FromStr;
use nym_credentials_interface::TicketType;
use crate::models::Client;
#[derive(Debug, PartialEq, sqlx::Type)]
@@ -15,6 +17,17 @@ pub enum ClientType {
ExitWireguard,
}
impl From<TicketType> for ClientType {
fn from(value: TicketType) -> Self {
match value {
TicketType::V1MixnetEntry => ClientType::EntryMixnet,
TicketType::V1MixnetExit => ClientType::ExitMixnet,
TicketType::V1WireguardEntry => ClientType::EntryWireguard,
TicketType::V1WireguardExit => ClientType::ExitWireguard,
}
}
}
impl FromStr for ClientType {
type Err = &'static str;
+14 -2
View File
@@ -20,6 +20,18 @@ pub enum GatewayStorageError {
#[error("the stored data associated with ticket {ticket_id} is malformed!")]
MalformedStoredTicketData { ticket_id: i64 },
#[error("Failed to convert from type of database: {0}")]
TypeConversion(String),
#[error("Failed to convert from type of database: {field_key}")]
TypeConversion { field_key: &'static str },
#[error("Serialization failure for {field_key}")]
Serialize {
field_key: &'static str,
source: bincode::Error,
},
#[error("Deserialization failure for {field_key}")]
Deserialize {
field_key: &'static str,
source: bincode::Error,
},
}
+77 -74
View File
@@ -1,6 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use async_trait::async_trait;
use bandwidth::BandwidthManager;
use clients::{ClientManager, ClientType};
use models::{
@@ -15,10 +16,10 @@ use sqlx::{
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
ConnectOptions,
};
use std::path::Path;
use std::{path::Path, time::Duration};
use tickets::TicketStorageManager;
use time::OffsetDateTime;
use tracing::{debug, error};
use tracing::{debug, error, log::LevelFilter};
pub mod bandwidth;
mod clients;
@@ -27,11 +28,21 @@ mod inboxes;
pub mod models;
mod shared_keys;
mod tickets;
pub mod traits;
mod wireguard_peers;
pub use error::GatewayStorageError;
pub use inboxes::InboxManager;
use crate::traits::{BandwidthGatewayStorage, InboxGatewayStorage, SharedKeyGatewayStorage};
fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options;
bincode::DefaultOptions::new()
.with_big_endian()
.with_varint_encoding()
}
// note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)]
pub struct GatewayStorage {
@@ -71,6 +82,21 @@ impl GatewayStorage {
&self.wireguard_peer_manager
}
pub async fn handle_forget_me(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), GatewayStorageError> {
let client_id = self.get_mixnet_client_id(client_address).await?;
self.inbox_manager()
.remove_messages_for_client(&client_address.as_base58_string())
.await?;
self.bandwidth_manager().remove_client(client_id).await?;
self.shared_key_manager()
.remove_shared_keys(&client_address.as_base58_string())
.await?;
Ok(())
}
/// Initialises `PersistentStorage` using the provided path.
///
/// # Arguments
@@ -92,6 +118,7 @@ impl GatewayStorage {
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.log_slow_statements(LevelFilter::Warn, Duration::from_millis(250))
.filename(database_path)
.create_if_missing(true)
.disable_statement_logging();
@@ -123,8 +150,9 @@ impl GatewayStorage {
}
}
impl GatewayStorage {
pub async fn get_mixnet_client_id(
#[async_trait]
impl SharedKeyGatewayStorage for GatewayStorage {
async fn get_mixnet_client_id(
&self,
client_address: DestinationAddressBytes,
) -> Result<i64, GatewayStorageError> {
@@ -134,22 +162,7 @@ impl GatewayStorage {
.await?)
}
pub async fn handle_forget_me(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), GatewayStorageError> {
let client_id = self.get_mixnet_client_id(client_address).await?;
self.inbox_manager()
.remove_messages_for_client(&client_address.as_base58_string())
.await?;
self.bandwidth_manager().remove_client(client_id).await?;
self.shared_key_manager()
.remove_shared_keys(&client_address.as_base58_string())
.await?;
Ok(())
}
pub async fn insert_shared_keys(
async fn insert_shared_keys(
&self,
client_address: DestinationAddressBytes,
shared_keys: &SharedGatewayKey,
@@ -178,7 +191,7 @@ impl GatewayStorage {
Ok(client_id)
}
pub async fn get_shared_keys(
async fn get_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<Option<PersistedSharedKeys>, GatewayStorageError> {
@@ -190,7 +203,7 @@ impl GatewayStorage {
}
#[allow(dead_code)]
pub async fn remove_shared_keys(
async fn remove_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), GatewayStorageError> {
@@ -200,7 +213,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn update_last_used_authentication_timestamp(
async fn update_last_used_authentication_timestamp(
&self,
client_id: i64,
last_used_authentication_timestamp: OffsetDateTime,
@@ -214,12 +227,15 @@ impl GatewayStorage {
Ok(())
}
pub async fn get_client(&self, client_id: i64) -> Result<Option<Client>, GatewayStorageError> {
async fn get_client(&self, client_id: i64) -> Result<Option<Client>, GatewayStorageError> {
let client = self.client_manager.get_client(client_id).await?;
Ok(client)
}
}
pub async fn store_message(
#[async_trait]
impl InboxGatewayStorage for GatewayStorage {
async fn store_message(
&self,
client_address: DestinationAddressBytes,
message: Vec<u8>,
@@ -230,7 +246,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn retrieve_messages(
async fn retrieve_messages(
&self,
client_address: DestinationAddressBytes,
start_after: Option<i64>,
@@ -242,19 +258,22 @@ impl GatewayStorage {
Ok(messages)
}
pub async fn remove_messages(&self, ids: Vec<i64>) -> Result<(), GatewayStorageError> {
async fn remove_messages(&self, ids: Vec<i64>) -> Result<(), GatewayStorageError> {
for id in ids {
self.inbox_manager.remove_message(id).await?;
}
Ok(())
}
}
pub async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), GatewayStorageError> {
#[async_trait]
impl BandwidthGatewayStorage for GatewayStorage {
async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), GatewayStorageError> {
self.bandwidth_manager.insert_new_client(client_id).await?;
Ok(())
}
pub async fn set_expiration(
async fn set_expiration(
&self,
client_id: i64,
expiration: OffsetDateTime,
@@ -265,12 +284,12 @@ impl GatewayStorage {
Ok(())
}
pub async fn reset_bandwidth(&self, client_id: i64) -> Result<(), GatewayStorageError> {
async fn reset_bandwidth(&self, client_id: i64) -> Result<(), GatewayStorageError> {
self.bandwidth_manager.reset_bandwidth(client_id).await?;
Ok(())
}
pub async fn get_available_bandwidth(
async fn get_available_bandwidth(
&self,
client_id: i64,
) -> Result<Option<PersistedBandwidth>, GatewayStorageError> {
@@ -280,7 +299,7 @@ impl GatewayStorage {
.await?)
}
pub async fn increase_bandwidth(
async fn increase_bandwidth(
&self,
client_id: i64,
amount: i64,
@@ -291,7 +310,7 @@ impl GatewayStorage {
.await?)
}
pub async fn revoke_ticket_bandwidth(
async fn revoke_ticket_bandwidth(
&self,
ticket_id: i64,
amount: i64,
@@ -302,7 +321,7 @@ impl GatewayStorage {
.await?)
}
pub async fn decrease_bandwidth(
async fn decrease_bandwidth(
&self,
client_id: i64,
amount: i64,
@@ -313,7 +332,7 @@ impl GatewayStorage {
.await?)
}
pub async fn insert_epoch_signers(
async fn insert_epoch_signers(
&self,
epoch_id: i64,
signer_ids: Vec<i64>,
@@ -324,7 +343,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn insert_received_ticket(
async fn insert_received_ticket(
&self,
client_id: i64,
received_at: OffsetDateTime,
@@ -344,11 +363,11 @@ impl GatewayStorage {
Ok(ticket_id)
}
pub async fn contains_ticket(&self, serial_number: &[u8]) -> Result<bool, GatewayStorageError> {
async fn contains_ticket(&self, serial_number: &[u8]) -> Result<bool, GatewayStorageError> {
Ok(self.ticket_manager.has_ticket_data(serial_number).await?)
}
pub async fn insert_ticket_verification(
async fn insert_ticket_verification(
&self,
ticket_id: i64,
signer_id: i64,
@@ -361,7 +380,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn update_rejected_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
async fn update_rejected_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
// set the ticket as rejected
self.ticket_manager.set_rejected_ticket(ticket_id).await?;
@@ -372,7 +391,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn update_verified_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
async fn update_verified_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
// 1. insert into verified table
self.ticket_manager
.insert_verified_ticket(ticket_id)
@@ -386,7 +405,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn remove_verified_ticket_binary_data(
async fn remove_verified_ticket_binary_data(
&self,
ticket_id: i64,
) -> Result<(), GatewayStorageError> {
@@ -396,7 +415,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn get_all_verified_tickets_with_sn(
async fn get_all_verified_tickets_with_sn(
&self,
) -> Result<Vec<VerifiedTicket>, GatewayStorageError> {
Ok(self
@@ -405,7 +424,7 @@ impl GatewayStorage {
.await?)
}
pub async fn get_all_proposed_tickets_with_sn(
async fn get_all_proposed_tickets_with_sn(
&self,
proposal_id: u32,
) -> Result<Vec<VerifiedTicket>, GatewayStorageError> {
@@ -415,7 +434,7 @@ impl GatewayStorage {
.await?)
}
pub async fn insert_redemption_proposal(
async fn insert_redemption_proposal(
&self,
tickets: &[VerifiedTicket],
proposal_id: u32,
@@ -438,7 +457,7 @@ impl GatewayStorage {
Ok(())
}
pub async fn clear_post_proposal_data(
async fn clear_post_proposal_data(
&self,
proposal_id: u32,
resolved_at: OffsetDateTime,
@@ -462,13 +481,11 @@ impl GatewayStorage {
Ok(())
}
pub async fn latest_proposal(&self) -> Result<Option<RedemptionProposal>, GatewayStorageError> {
async fn latest_proposal(&self) -> Result<Option<RedemptionProposal>, GatewayStorageError> {
Ok(self.ticket_manager.get_latest_redemption_proposal().await?)
}
pub async fn get_all_unverified_tickets(
&self,
) -> Result<Vec<ClientTicket>, GatewayStorageError> {
async fn get_all_unverified_tickets(&self) -> Result<Vec<ClientTicket>, GatewayStorageError> {
self.ticket_manager
.get_unverified_tickets()
.await?
@@ -477,21 +494,21 @@ impl GatewayStorage {
.collect()
}
pub async fn get_all_unresolved_proposals(&self) -> Result<Vec<i64>, GatewayStorageError> {
async fn get_all_unresolved_proposals(&self) -> Result<Vec<i64>, GatewayStorageError> {
Ok(self
.ticket_manager
.get_all_unresolved_redemption_proposal_ids()
.await?)
}
pub async fn get_votes(&self, ticket_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
async fn get_votes(&self, ticket_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
Ok(self
.ticket_manager
.get_verification_votes(ticket_id)
.await?)
}
pub async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
Ok(self.ticket_manager.get_epoch_signers(epoch_id).await?)
}
@@ -500,34 +517,20 @@ impl GatewayStorage {
/// # Arguments
///
/// * `peer`: wireguard peer data to be stored
/// * `with_client_id`: if the peer should have a corresponding client_id
/// (created with entry wireguard ticket) or live without one (or with an
/// exiting one), for temporary backwards compatibility.
pub async fn insert_wireguard_peer(
async fn insert_wireguard_peer(
&self,
peer: &defguard_wireguard_rs::host::Peer,
with_client_id: bool,
) -> Result<Option<i64>, GatewayStorageError> {
client_type: ClientType,
) -> Result<i64, GatewayStorageError> {
let client_id = match self
.wireguard_peer_manager
.retrieve_peer(&peer.public_key.to_string())
.await?
{
Some(peer) => peer.client_id,
_ => {
if with_client_id {
Some(
self.client_manager
.insert_client(ClientType::EntryWireguard)
.await?,
)
} else {
None
}
}
None => self.client_manager.insert_client(client_type).await?,
};
let mut peer = WireguardPeer::from(peer.clone());
peer.client_id = client_id;
let peer = WireguardPeer::from_defguard_peer(peer.clone(), client_id)?;
self.wireguard_peer_manager.insert_peer(&peer).await?;
Ok(client_id)
}
@@ -537,7 +540,7 @@ impl GatewayStorage {
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be retrieved.
pub async fn get_wireguard_peer(
async fn get_wireguard_peer(
&self,
peer_public_key: &str,
) -> Result<Option<WireguardPeer>, GatewayStorageError> {
@@ -549,7 +552,7 @@ impl GatewayStorage {
}
/// Retrieves all wireguard peers.
pub async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, GatewayStorageError> {
async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, GatewayStorageError> {
let ret = self.wireguard_peer_manager.retrieve_all_peers().await?;
Ok(ret)
}
@@ -559,7 +562,7 @@ impl GatewayStorage {
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be removed.
pub async fn remove_wireguard_peer(
async fn remove_wireguard_peer(
&self,
peer_public_key: &str,
) -> Result<(), GatewayStorageError> {
+25 -67
View File
@@ -1,9 +1,7 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use std::time::SystemTime;
use crate::error::GatewayStorageError;
use crate::{error::GatewayStorageError, make_bincode_serializer};
use nym_credentials_interface::{AvailableBandwidth, ClientTicket, CredentialSpendingData};
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
use sqlx::FromRow;
@@ -112,35 +110,24 @@ impl TryFrom<UnverifiedTicketData> for ClientTicket {
#[derive(Debug, Clone, FromRow)]
pub struct WireguardPeer {
pub public_key: String,
pub preshared_key: Option<String>,
pub protocol_version: Option<i64>,
pub endpoint: Option<String>,
pub last_handshake: Option<OffsetDateTime>,
pub tx_bytes: i64,
pub rx_bytes: i64,
pub persistent_keepalive_interval: Option<i64>,
pub allowed_ips: Vec<u8>,
pub client_id: Option<i64>,
pub client_id: i64,
}
impl From<defguard_wireguard_rs::host::Peer> for WireguardPeer {
fn from(value: defguard_wireguard_rs::host::Peer) -> Self {
WireguardPeer {
impl WireguardPeer {
pub fn from_defguard_peer(
value: defguard_wireguard_rs::host::Peer,
client_id: i64,
) -> Result<Self, crate::error::GatewayStorageError> {
Ok(WireguardPeer {
public_key: value.public_key.to_string(),
preshared_key: value.preshared_key.as_ref().map(|k| k.to_string()),
protocol_version: value.protocol_version.map(|v| v as i64),
endpoint: value.endpoint.map(|e| e.to_string()),
last_handshake: value.last_handshake.map(OffsetDateTime::from),
tx_bytes: value.tx_bytes as i64,
rx_bytes: value.rx_bytes as i64,
persistent_keepalive_interval: value.persistent_keepalive_interval.map(|v| v as i64),
allowed_ips: bincode::Options::serialize(
bincode::DefaultOptions::new(),
&value.allowed_ips,
)
.unwrap_or_default(),
client_id: None,
}
allowed_ips: bincode::Options::serialize(make_bincode_serializer(), &value.allowed_ips)
.map_err(|source| crate::error::GatewayStorageError::Serialize {
field_key: "allowed_ips",
source,
})?,
client_id,
})
}
}
@@ -149,49 +136,20 @@ impl TryFrom<WireguardPeer> for defguard_wireguard_rs::host::Peer {
fn try_from(value: WireguardPeer) -> Result<Self, Self::Error> {
Ok(Self {
public_key: value
.public_key
.as_str()
.try_into()
.map_err(|e| Self::Error::TypeConversion(format!("public key {e}")))?,
preshared_key: value
.preshared_key
.as_deref()
.map(TryFrom::try_from)
.transpose()
.map_err(|e| Self::Error::TypeConversion(format!("preshared key {e}")))?,
protocol_version: value
.protocol_version
.map(TryFrom::try_from)
.transpose()
.map_err(|e| Self::Error::TypeConversion(format!("protocol version {e}")))?,
endpoint: value
.endpoint
.as_deref()
.map(|e| e.parse())
.transpose()
.map_err(|e| Self::Error::TypeConversion(format!("endpoint {e}")))?,
last_handshake: value.last_handshake.map(SystemTime::from),
tx_bytes: value
.tx_bytes
.try_into()
.map_err(|e| Self::Error::TypeConversion(format!("tx bytes {e}")))?,
rx_bytes: value
.rx_bytes
.try_into()
.map_err(|e| Self::Error::TypeConversion(format!("rx bytes {e}")))?,
persistent_keepalive_interval: value
.persistent_keepalive_interval
.map(TryFrom::try_from)
.transpose()
.map_err(|e| {
Self::Error::TypeConversion(format!("persistent keepalive interval {e}"))
})?,
public_key: value.public_key.as_str().try_into().map_err(|_| {
Self::Error::TypeConversion {
field_key: "public_key",
}
})?,
allowed_ips: bincode::Options::deserialize(
bincode::DefaultOptions::new(),
&value.allowed_ips,
)
.map_err(|e| Self::Error::TypeConversion(format!("allowed ips {e}")))?,
.map_err(|source| Self::Error::Deserialize {
field_key: "allowed_ips",
source,
})?,
..Default::default()
})
}
}
+511
View File
@@ -0,0 +1,511 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use async_trait::async_trait;
use nym_credentials_interface::ClientTicket;
use nym_gateway_requests::SharedGatewayKey;
use nym_sphinx::DestinationAddressBytes;
use time::OffsetDateTime;
use crate::{
clients::ClientType,
models::{
Client, PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage,
VerifiedTicket, WireguardPeer,
},
GatewayStorageError,
};
#[async_trait]
pub trait SharedKeyGatewayStorage {
async fn get_mixnet_client_id(
&self,
client_address: DestinationAddressBytes,
) -> Result<i64, GatewayStorageError>;
async fn insert_shared_keys(
&self,
client_address: DestinationAddressBytes,
shared_keys: &SharedGatewayKey,
) -> Result<i64, GatewayStorageError>;
async fn get_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<Option<PersistedSharedKeys>, GatewayStorageError>;
#[allow(dead_code)]
async fn remove_shared_keys(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), GatewayStorageError>;
async fn update_last_used_authentication_timestamp(
&self,
client_id: i64,
last_used_authentication_timestamp: OffsetDateTime,
) -> Result<(), GatewayStorageError>;
async fn get_client(&self, client_id: i64) -> Result<Option<Client>, GatewayStorageError>;
}
#[async_trait]
pub trait InboxGatewayStorage {
async fn store_message(
&self,
client_address: DestinationAddressBytes,
message: Vec<u8>,
) -> Result<(), GatewayStorageError>;
async fn retrieve_messages(
&self,
client_address: DestinationAddressBytes,
start_after: Option<i64>,
) -> Result<(Vec<StoredMessage>, Option<i64>), GatewayStorageError>;
async fn remove_messages(&self, ids: Vec<i64>) -> Result<(), GatewayStorageError>;
}
#[async_trait]
pub trait BandwidthGatewayStorage: dyn_clone::DynClone {
async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), GatewayStorageError>;
async fn set_expiration(
&self,
client_id: i64,
expiration: OffsetDateTime,
) -> Result<(), GatewayStorageError>;
async fn reset_bandwidth(&self, client_id: i64) -> Result<(), GatewayStorageError>;
async fn get_available_bandwidth(
&self,
client_id: i64,
) -> Result<Option<PersistedBandwidth>, GatewayStorageError>;
async fn increase_bandwidth(
&self,
client_id: i64,
amount: i64,
) -> Result<i64, GatewayStorageError>;
async fn revoke_ticket_bandwidth(
&self,
ticket_id: i64,
amount: i64,
) -> Result<(), GatewayStorageError>;
async fn decrease_bandwidth(
&self,
client_id: i64,
amount: i64,
) -> Result<i64, GatewayStorageError>;
async fn insert_epoch_signers(
&self,
epoch_id: i64,
signer_ids: Vec<i64>,
) -> Result<(), GatewayStorageError>;
async fn insert_received_ticket(
&self,
client_id: i64,
received_at: OffsetDateTime,
serial_number: Vec<u8>,
data: Vec<u8>,
) -> Result<i64, GatewayStorageError>;
async fn contains_ticket(&self, serial_number: &[u8]) -> Result<bool, GatewayStorageError>;
async fn insert_ticket_verification(
&self,
ticket_id: i64,
signer_id: i64,
verified_at: OffsetDateTime,
accepted: bool,
) -> Result<(), GatewayStorageError>;
async fn update_rejected_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError>;
async fn update_verified_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError>;
async fn remove_verified_ticket_binary_data(
&self,
ticket_id: i64,
) -> Result<(), GatewayStorageError>;
async fn get_all_verified_tickets_with_sn(
&self,
) -> Result<Vec<VerifiedTicket>, GatewayStorageError>;
async fn get_all_proposed_tickets_with_sn(
&self,
proposal_id: u32,
) -> Result<Vec<VerifiedTicket>, GatewayStorageError>;
async fn insert_redemption_proposal(
&self,
tickets: &[VerifiedTicket],
proposal_id: u32,
created_at: OffsetDateTime,
) -> Result<(), GatewayStorageError>;
async fn clear_post_proposal_data(
&self,
proposal_id: u32,
resolved_at: OffsetDateTime,
rejected: bool,
) -> Result<(), GatewayStorageError>;
async fn latest_proposal(&self) -> Result<Option<RedemptionProposal>, GatewayStorageError>;
async fn get_all_unverified_tickets(&self) -> Result<Vec<ClientTicket>, GatewayStorageError>;
async fn get_all_unresolved_proposals(&self) -> Result<Vec<i64>, GatewayStorageError>;
async fn get_votes(&self, ticket_id: i64) -> Result<Vec<i64>, GatewayStorageError>;
async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, GatewayStorageError>;
/// Insert a wireguard peer in the storage.
///
/// # Arguments
///
/// * `peer`: wireguard peer data to be stored
async fn insert_wireguard_peer(
&self,
peer: &defguard_wireguard_rs::host::Peer,
client_type: ClientType,
) -> Result<i64, GatewayStorageError>;
/// Tries to retrieve a particular peer with the given public key.
///
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be retrieved.
async fn get_wireguard_peer(
&self,
peer_public_key: &str,
) -> Result<Option<WireguardPeer>, GatewayStorageError>;
/// Retrieves all wireguard peers.
async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, GatewayStorageError>;
/// Remove a wireguard peer from the storage.
///
/// # Arguments
///
/// * `peer_public_key`: wireguard public key of the peer to be removed.
async fn remove_wireguard_peer(&self, peer_public_key: &str)
-> Result<(), GatewayStorageError>;
}
#[cfg(feature = "mock")]
pub mod mock {
use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock;
use super::*;
struct EcashSigner {
_epoch_id: i64,
_signer_id: i64,
}
struct ReceivedTicket {
client_id: i64,
_received_at: OffsetDateTime,
rejected: Option<bool>,
}
struct TicketData {
serial_number: Vec<u8>,
data: Option<Vec<u8>>,
}
struct TicketVerification {
_ticket_id: i64,
_signer_id: i64,
_verified_at: OffsetDateTime,
_accepted: bool,
}
#[derive(Default)]
pub struct MockGatewayStorage {
available_bandwidth: HashMap<i64, PersistedBandwidth>,
ecash_signers: Vec<EcashSigner>,
received_ticket: HashMap<i64, ReceivedTicket>,
ticket_data: HashMap<i64, TicketData>,
ticket_verification: HashMap<i64, TicketVerification>,
verified_tickets: Vec<i64>,
wireguard_peers: HashMap<String, WireguardPeer>,
clients: HashMap<i64, String>,
}
#[async_trait]
impl BandwidthGatewayStorage for Arc<RwLock<MockGatewayStorage>> {
async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), GatewayStorageError> {
self.write().await.available_bandwidth.insert(
client_id,
PersistedBandwidth {
client_id,
available: 0,
expiration: Some(OffsetDateTime::UNIX_EPOCH),
},
);
Ok(())
}
async fn set_expiration(
&self,
client_id: i64,
expiration: OffsetDateTime,
) -> Result<(), GatewayStorageError> {
if let Some(bw) = self.write().await.available_bandwidth.get_mut(&client_id) {
bw.expiration = Some(expiration);
}
Ok(())
}
async fn reset_bandwidth(&self, client_id: i64) -> Result<(), GatewayStorageError> {
if let Some(bw) = self.write().await.available_bandwidth.get_mut(&client_id) {
bw.available = 0;
bw.expiration = Some(OffsetDateTime::UNIX_EPOCH);
}
Ok(())
}
async fn get_available_bandwidth(
&self,
client_id: i64,
) -> Result<Option<PersistedBandwidth>, GatewayStorageError> {
Ok(self
.read()
.await
.available_bandwidth
.get(&client_id)
.cloned())
}
async fn increase_bandwidth(
&self,
client_id: i64,
amount: i64,
) -> Result<i64, GatewayStorageError> {
self.write()
.await
.available_bandwidth
.get_mut(&client_id)
.map(|bw| {
bw.available += amount;
bw.available
})
.ok_or(GatewayStorageError::InternalDatabaseError(
sqlx::Error::RowNotFound,
))
}
async fn revoke_ticket_bandwidth(
&self,
ticket_id: i64,
amount: i64,
) -> Result<(), GatewayStorageError> {
let mut guard = self.write().await;
if let Some(client_id) = guard
.received_ticket
.get(&ticket_id)
.map(|ticket| ticket.client_id)
{
if let Some(bw) = guard.available_bandwidth.get_mut(&client_id) {
bw.available -= amount;
}
}
Ok(())
}
async fn decrease_bandwidth(
&self,
client_id: i64,
amount: i64,
) -> Result<i64, GatewayStorageError> {
self.write()
.await
.available_bandwidth
.get_mut(&client_id)
.map(|bw| {
bw.available -= amount;
bw.available
})
.ok_or(GatewayStorageError::InternalDatabaseError(
sqlx::Error::RowNotFound,
))
}
async fn insert_epoch_signers(
&self,
_epoch_id: i64,
signer_ids: Vec<i64>,
) -> Result<(), GatewayStorageError> {
self.write()
.await
.ecash_signers
.extend(signer_ids.iter().map(|signer_id| EcashSigner {
_epoch_id,
_signer_id: *signer_id,
}));
Ok(())
}
async fn insert_received_ticket(
&self,
client_id: i64,
_received_at: OffsetDateTime,
serial_number: Vec<u8>,
data: Vec<u8>,
) -> Result<i64, GatewayStorageError> {
let mut guard = self.write().await;
let ticket_id = guard.received_ticket.len() as i64;
guard.received_ticket.insert(
ticket_id,
ReceivedTicket {
client_id,
_received_at,
rejected: None,
},
);
guard.ticket_data.insert(
ticket_id,
TicketData {
serial_number,
data: Some(data),
},
);
Ok(ticket_id)
}
async fn contains_ticket(&self, serial_number: &[u8]) -> Result<bool, GatewayStorageError> {
Ok(self
.read()
.await
.ticket_data
.values()
.any(|ticket_data| ticket_data.serial_number == serial_number))
}
async fn insert_ticket_verification(
&self,
_ticket_id: i64,
_signer_id: i64,
_verified_at: OffsetDateTime,
_accepted: bool,
) -> Result<(), GatewayStorageError> {
self.write().await.ticket_verification.insert(
_ticket_id,
TicketVerification {
_ticket_id,
_signer_id,
_verified_at,
_accepted,
},
);
Ok(())
}
async fn update_rejected_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
let mut guard = self.write().await;
if let Some(ticket) = guard.received_ticket.get_mut(&ticket_id) {
ticket.rejected = Some(true);
}
guard.ticket_data.remove(&ticket_id);
Ok(())
}
async fn update_verified_ticket(&self, ticket_id: i64) -> Result<(), GatewayStorageError> {
let mut guard = self.write().await;
guard.verified_tickets.push(ticket_id);
guard.ticket_verification.remove(&ticket_id);
Ok(())
}
async fn remove_verified_ticket_binary_data(
&self,
ticket_id: i64,
) -> Result<(), GatewayStorageError> {
if let Some(ticket) = self.write().await.ticket_data.get_mut(&ticket_id) {
ticket.data = None;
}
Ok(())
}
async fn get_all_verified_tickets_with_sn(
&self,
) -> Result<Vec<VerifiedTicket>, GatewayStorageError> {
todo!()
}
async fn get_all_proposed_tickets_with_sn(
&self,
_proposal_id: u32,
) -> Result<Vec<VerifiedTicket>, GatewayStorageError> {
todo!()
}
async fn insert_redemption_proposal(
&self,
_tickets: &[VerifiedTicket],
_proposal_id: u32,
_created_at: OffsetDateTime,
) -> Result<(), GatewayStorageError> {
todo!()
}
async fn clear_post_proposal_data(
&self,
_proposal_id: u32,
_resolved_at: OffsetDateTime,
_rejected: bool,
) -> Result<(), GatewayStorageError> {
todo!()
}
async fn latest_proposal(&self) -> Result<Option<RedemptionProposal>, GatewayStorageError> {
todo!()
}
async fn get_all_unverified_tickets(
&self,
) -> Result<Vec<ClientTicket>, GatewayStorageError> {
todo!()
}
async fn get_all_unresolved_proposals(&self) -> Result<Vec<i64>, GatewayStorageError> {
todo!()
}
async fn get_votes(&self, _ticket_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
todo!()
}
async fn get_signers(&self, _epoch_id: i64) -> Result<Vec<i64>, GatewayStorageError> {
todo!()
}
async fn insert_wireguard_peer(
&self,
peer: &defguard_wireguard_rs::host::Peer,
client_type: ClientType,
) -> Result<i64, GatewayStorageError> {
let mut guard = self.write().await;
let client_id =
if let Some(peer) = guard.wireguard_peers.get(&peer.public_key.to_string()) {
peer.client_id
} else {
let client_id = guard.clients.len() as i64;
guard.clients.insert(client_id, client_type.to_string());
client_id
};
guard.wireguard_peers.insert(
peer.public_key.to_string(),
WireguardPeer::from_defguard_peer(peer.clone(), client_id)?,
);
Ok(client_id)
}
async fn get_wireguard_peer(
&self,
peer_public_key: &str,
) -> Result<Option<WireguardPeer>, GatewayStorageError> {
Ok(self
.read()
.await
.wireguard_peers
.get(peer_public_key)
.cloned())
}
async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, GatewayStorageError> {
todo!()
}
async fn remove_wireguard_peer(
&self,
peer_public_key: &str,
) -> Result<(), GatewayStorageError> {
self.write().await.wireguard_peers.remove(peer_public_key);
Ok(())
}
}
}
@@ -27,15 +27,18 @@ impl WgPeerManager {
pub(crate) async fn insert_peer(&self, peer: &WireguardPeer) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT OR IGNORE INTO wireguard_peer(public_key, preshared_key, protocol_version, endpoint, last_handshake, tx_bytes, rx_bytes, persistent_keepalive_interval, allowed_ips, client_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
INSERT OR IGNORE INTO wireguard_peer(public_key, allowed_ips, client_id)
VALUES (?, ?, ?);
UPDATE wireguard_peer
SET preshared_key = ?, protocol_version = ?, endpoint = ?, last_handshake = ?, tx_bytes = ?, rx_bytes = ?, persistent_keepalive_interval = ?, allowed_ips = ?, client_id = ?
SET allowed_ips = ?, client_id = ?
WHERE public_key = ?
"#,
peer.public_key, peer.preshared_key, peer.protocol_version, peer.endpoint, peer.last_handshake, peer.tx_bytes, peer.rx_bytes, peer.persistent_keepalive_interval, peer.allowed_ips, peer.client_id,
peer.preshared_key, peer.protocol_version, peer.endpoint, peer.last_handshake, peer.tx_bytes, peer.rx_bytes, peer.persistent_keepalive_interval, peer.allowed_ips, peer.client_id,
peer.public_key,
peer.allowed_ips,
peer.client_id,
peer.allowed_ips,
peer.client_id,
peer.public_key,
)
.execute(&self.connection_pool)
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-http-api-client"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+57 -28
View File
@@ -6,9 +6,9 @@
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
//! supporting DoH and DoT.
//!
//! This resolver implements a fallback mechanism where, should the DNS-over-TLS resolution fail, a
//! This resolver supports a fallback mechanism where, should the DNS-over-TLS resolution fail, a
//! followup resolution will be done using the hosts configured default (e.g. `/etc/resolve.conf` on
//! linux).
//! linux). This is disabled by default and can be enabled using [`enable_system_fallback`].
//!
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
//! `hickory-resolver` crate
@@ -93,14 +93,14 @@ pub struct HickoryDnsResolver {
// Tokio Runtime in initialization, so we must delay the actual
// construction of the resolver.
state: Arc<OnceCell<TokioResolver>>,
fallback: Arc<OnceCell<TokioResolver>>,
fallback: Option<Arc<OnceCell<TokioResolver>>>,
dont_use_shared: bool,
}
impl Resolve for HickoryDnsResolver {
fn resolve(&self, name: Name) -> Resolving {
let resolver = self.state.clone();
let fallback = self.fallback.clone();
let maybe_fallback = self.fallback.clone();
let independent = self.dont_use_shared;
Box::pin(async move {
let resolver = resolver.get_or_try_init(|| {
@@ -117,23 +117,30 @@ impl Resolve for HickoryDnsResolver {
let lookup = match resolver.lookup_ip(name.as_str()).await {
Ok(res) => res,
Err(e) => {
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
}
let resolver = fallback.get_or_try_init(|| {
// using a closure here is slightly gross, but this makes sure that if the
// lazy-init returns an error it can be handled by the client
if independent {
new_resolver_system()
} else {
Ok(SHARED_RESOLVER
.fallback
.get_or_try_init(new_resolver_system)?
.clone())
if let Some(ref fallback) = maybe_fallback {
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
}
})?;
resolver.lookup_ip(name.as_str()).await?
let resolver = fallback.get_or_try_init(|| {
// using a closure here is slightly gross, but this makes sure that if the
// lazy-init returns an error it can be handled by the client
if independent {
new_resolver_system()
} else {
Ok(SHARED_RESOLVER
.fallback
.as_ref()
.ok_or(e)? // if the shared resolver has no fallback return the original error
.get_or_try_init(new_resolver_system)?
.clone())
}
})?;
resolver.lookup_ip(name.as_str()).await?
} else {
return Err(e.into());
}
}
};
@@ -162,14 +169,17 @@ impl HickoryDnsResolver {
let lookup = match resolver.lookup_ip(name).await {
Ok(res) => res,
Err(e) => {
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
if let Some(ref fallback) = self.fallback {
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
}
let resolver = fallback.get_or_try_init(|| self.new_resolver_system())?;
resolver.lookup_ip(name).await?
} else {
return Err(e.into());
}
let resolver = self
.fallback
.get_or_try_init(|| self.new_resolver_system())?;
resolver.lookup_ip(name).await?
}
};
@@ -193,15 +203,34 @@ impl HickoryDnsResolver {
}
fn new_resolver_system(&self) -> Result<TokioResolver, HickoryDnsError> {
if self.dont_use_shared {
if self.dont_use_shared || SHARED_RESOLVER.fallback.is_none() {
new_resolver_system()
} else {
Ok(SHARED_RESOLVER
.fallback
.as_ref()
.unwrap()
.get_or_try_init(new_resolver_system)?
.clone())
}
}
/// Enable fallback to the system default resolver if the primary (DoX) resolver fails
pub fn enable_system_fallback(&mut self) -> Result<(), HickoryDnsError> {
self.fallback = Some(Default::default());
let _ = self
.fallback
.as_ref()
.unwrap()
.get_or_try_init(new_resolver_system)?;
Ok(())
}
/// Disable fallback resolution. If the primary resolver fails the error is
/// returned immediately
pub fn disable_system_fallback(&mut self) {
self.fallback = None;
}
}
/// Create a new resolver with a custom DoT based configuration. The options are overridden to look
+1 -1
View File
@@ -16,7 +16,7 @@ pub struct UserAgent {
pub version: String,
/// client platform
pub platform: String,
/// source commit version for the calling calling crate / subsystem
/// source commit version for the calling crate / subsystem
pub git_commit: String,
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-http-api-common"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-inclusion-probability"
version = "0.1.0"
version.workspace = true
description = "Nym active set probability simulator"
edition.workspace = true
authors.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-ip-packet-requests"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-ledger"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnode-common"
version = "0.1.0"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-network-defaults"
version = "0.1.0"
version.workspace = true
description = "Nym network defaults"
edition.workspace = true
authors.workspace = true
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-node-tester-utils"
version = "0.1.0"
version.workspace = true
edition = "2021"
license.workspace = true
@@ -28,5 +28,5 @@ nym-sphinx = { path = "../nymsphinx" }
workspace = true
## wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
[target."cfg(target_arch = \"wasm32\")".dependencies.nym-wasm-utils]
path = "../wasm/utils"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-nonexhaustive-delayqueue"
version = "0.1.0"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-id"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-metrics"
version = "0.1.0"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-compact-ecash"
version = "0.1.0"
version.workspace = true
authors = ["Ania Piotrowska <ania@nymtech.net>"]
edition = "2021"
license = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-noise"
version = "0.1.0"
version.workspace = true
authors = ["Simon Wicky <simon@nymtech.net>"]
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-noise-keys"
version = "0.1.0"
version.workspace = true
authors = ["Simon Wicky <simon@nymtech.net>"]
edition = "2021"
license.workspace = true
+5 -13
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx"
version = "0.1.0"
version.workspace = true
description = "Top-level crate for sphinx packets as used by the Nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
@@ -26,15 +26,13 @@ nym-sphinx-types = { path = "types" }
# those dependencies are due to intriducing preparer and receiver. Perpaphs that indicates they should be moved
# to separate crate?
nym-crypto = { path = "../crypto", version = "0.4.0" }
nym-crypto = { path = "../crypto" }
nym-topology = { path = "../topology" }
nym-metrics = { path = "../nym-metrics" }
[dev-dependencies]
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-crypto = { path = "../crypto", version = "0.4.0", features = [
"asymmetric",
] }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
# net2 via tokio-util -> tokio -> mio -> net2
@@ -47,11 +45,5 @@ features = ["sync"]
[features]
default = ["sphinx"]
sphinx = [
"nym-sphinx-params/sphinx",
"nym-sphinx-types/sphinx",
]
outfox = [
"nym-sphinx-params/outfox",
"nym-sphinx-types/outfox",
]
sphinx = ["nym-sphinx-params/sphinx", "nym-sphinx-types/sphinx"]
outfox = ["nym-sphinx-params/outfox", "nym-sphinx-types/outfox"]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-acknowledgements"
version = "0.1.0"
version.workspace = true
description = "Sphinx packet ack messages"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-addressing"
version = "0.1.0"
version.workspace = true
description = "Nym mixnet addressing"
edition = { workspace = true }
authors = { workspace = true }
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-anonymous-replies"
version = "0.1.0"
version.workspace = true
description = "Anonymous sphinx packet replies using singly-use-reply-blocks (SURB)"
edition = { workspace = true }
authors = { workspace = true }
+2 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-chunking"
version = "0.1.0"
version.workspace = true
description = "Sphinx packet chunking of underlying data packets"
edition = { workspace = true }
authors = { workspace = true }
@@ -21,6 +21,4 @@ nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" }
nym-sphinx-types = { path = "../types" }
nym-metrics = { path = "../../nym-metrics" }
nym-crypto = { path = "../../crypto", version = "0.4.0", features = [
"asymmetric",
] }
nym-crypto = { path = "../../crypto", features = ["asymmetric"] }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-cover"
version = "0.1.0"
version.workspace = true
description = "Sphinx packets used as cover traffic"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-forwarding"
version = "0.1.0"
version.workspace = true
description = "Sphinx packet forwarding as Nym mix packets"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-framing"
version = "0.1.0"
version.workspace = true
description = "Sphinx packet framing for the Nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-params"
version = "0.1.0"
version.workspace = true
description = "Sphinx packet parameters for the Nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-routing"
version = "0.1.0"
version.workspace = true
description = "Sphinx packet routing as Nym mix packets"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-sphinx-types"
version = "0.2.0"
version.workspace = true
description = "Re-export sphinx packet types"
edition = { workspace = true }
authors = { workspace = true }

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