Compare commits

...

87 Commits

Author SHA1 Message Date
mfahampshire 01f1a7c3d0 Merge branch 'develop' into max/mixfetch-edge-runtime-debug 2026-04-17 15:15:18 +00:00
dynco-nym 7140ba4ea9 Fix invalid ticket spend (#6683)
* Fix

* PR feedback

* Bump version

* Update sqlx files
2026-04-17 16:49:17 +02:00
benedetta davico 62962509eb Merge pull request #6686 from nymtech/workflow/fix
Fixes to crates and CI
2026-04-17 16:21:12 +02:00
mfahampshire d285b70030 Specify Rust v1.85 for all contract crates (test) 2026-04-17 13:04:14 +01:00
dynco-nym 534a5068d3 Return ipv6 addresses as well (#6684)
* Return ipv6 addresses as well

* Fix clippy

* Bump NS API version
2026-04-17 13:42:48 +02:00
mfahampshire d2a7199b07 revert changes to package.json + yarn.lock 2026-04-17 11:23:28 +01:00
mfahampshire dd6a45f251 Make publication explicit 2026-04-17 09:23:55 +01:00
mfahampshire 6ee1f16ce8 Canonical ordering lefthook checker 2026-04-17 08:17:18 +01:00
mfahampshire 924d7d1ccc Enforce ordering of [package] fields in cargo.toml files 2026-04-17 07:49:50 +01:00
mfahampshire 395c134186 Cargo.toml package field fixes for preflight check 2026-04-16 14:02:57 +01:00
benedetta davico 55e485ebce Update Cargo.toml 2026-04-16 13:16:37 +02:00
benedetta davico cfce6dedff Update Cargo.toml 2026-04-16 13:13:35 +02:00
benedetta davico 5a08a4cdd2 Enable publishing for nym-lp Cargo package 2026-04-16 13:11:07 +02:00
benedetta davico 42ffb7d36e Enable publishing for nym-kkt-ciphersuite 2026-04-16 13:10:51 +02:00
benedettadavico e024d68fac mebbe 2026-04-16 12:28:16 +02:00
benedettadavico e66a069d5f add file 2026-04-16 12:28:15 +02:00
benedettadavico 3531901a17 ? 2026-04-16 12:28:15 +02:00
benedettadavico a399a75b03 test.. 2026-04-16 12:28:14 +02:00
benedettadavico 0de14718cb attempt at fix 2026-04-16 12:28:14 +02:00
mfahampshire 06ec97c08f Add gitignore - do not push local test results to remote 2026-04-15 18:39:46 +01:00
mfahampshire b5d20199a0 Add Playwright CI tests for mix-fetch internal-dev harness
Per-PR smoke test (ci-sdk-wasm.yml): loads WASM runtimes and inits
MixFetch on Chromium, Firefox, and WebKit.

Nightly stress test (nightly-mix-fetch-stress.yml): connects to mainnet
via random gateway, runs 10 mixed-size fetches, asserts >= 80% pass.
2026-04-15 18:39:44 +01:00
mfahampshire 9790033f7b Fix deprecated wasm_bindgen init call in internal-dev worker 2026-04-15 18:32:59 +01:00
mfahampshire 3dd1658c08 Downgrade misleading late-packet log from error to warning in mix-fetch 2026-04-15 18:32:59 +01:00
mfahampshire 9d40039f88 Simplify internal-dev test harness 2026-04-15 18:32:59 +01:00
mfahampshire 9f013aaab0 Revert formatting changes to make PR less noisy 2026-04-15 18:32:59 +01:00
mfahampshire b0f5945ba8 Fix erroneous versioning bump + version in internal-dev 2026-04-15 18:32:58 +01:00
mfahampshire e656157284 Make internal-dev less debug specific
- Evergreen the drip test
- Add setup config to MixFetch instance
2026-04-15 18:32:58 +01:00
mfahampshire 114e9b2f15 Version bump 2026-04-15 18:32:58 +01:00
mfahampshire 2274dd4eeb Fix cross-runtime panic in mix-fetch when timed-out requests receive
late error injection
- Add early return in reject() when request is already gone from Rust
  map
- Replace panic with log + return in SendError and InjectData
2026-04-15 18:32:58 +01:00
mfahampshire be5cdb1ebe Debug 2026-04-15 18:32:58 +01:00
mfahampshire cd476ef6a2 Update libcrux crates to use versions published on crates.io instead of
git import
2026-04-15 17:30:57 +01:00
dynco-nym ad56645fc5 Block non-public IPR/NR checks (#6670)
* Block non-public IPR/NR checks

* Add CLI override flag
2026-04-15 15:59:38 +02:00
mfahampshire 7ceaf9a40e Max/mixtcp (#6321)
* Add mixtcp crate 

Components:
- NymIprDevice: smoltcp::phy::Device impl using channel-based I/O
- NymIprBridge: async task bridging the device to IpMixStream
- create_device(): helper to set up the complete stack

* - Cleanup
- Add graceful shutdown
- Declutter logging - move a lot of bridge info! -> trace!
- Move rustls, nym-bin-common, bytes to dev-dependencies
- Extract TlsOverTcp to mod.rs
- Make timing more granular
- Update readme

* Add UDP example

* Add UDP example to readme

* rename mixtcp -> smolmix

* Add Tunnel API with TcpStream and UdpSocket over tokio-smoltcp

* Re-export Tunnel API and add init_logging convenience function

* Remove raw smoltcp path, flatten tunnel module

* Clean up bridge, device, and tunnel code

* Consolidate architecture docs, tidy examples and README

- Add src/ARCHITECTURE.md as single source of truth for architecture
- Include in docs.rs via doc = include_str!
- Strip duplicated diagrams from tunnel.rs, device.rs, README
- Extract tls_connector() helper in HTTPS example to match websocket example
- Use consistent 'smolmix' casing in README

* Update smolmix imports for ipr_wrapper API

- stream_wrapper::{IpMixStream, NetworkEnvironment} → ipr_wrapper::
- connect_tunnel() → check_connected()
- disconnect_stream() → disconnect()
- allocated_ips() returns &IpPair directly (no Option)

* Add Tunnel::new_with_ipr, re-export IpPair/Recipient, tidy examples

- Add Tunnel::new_with_ipr() for targeting a specific exit node
- Re-export IpPair and Recipient so users don't need direct deps
- Add DNS leak warning to WebSocket example
- Await hyper connection task in HTTPS example

* Restructure smolmix into multi-crate workspace

- Move core tunnel code to smolmix/core/- Rewrite examples for each crate with clearnet/mixnet comparisons

* Add workspace README with architecture overview

* Update nym-sdk README module descriptions

- Replace stale stream_wrapper description with ipr_wrapper + mixnet::stream
- Remove TODO comment

* Remove companion crates, scope to smolmix-core

* Comment out additional components on -core branch README.md

* Cargo.lock fix for compilation issue

* Downgrade accidentally bumped dependencies in Cargo lock + change
smolmix dependencies to import from workspace

* Fix workspace deps + move nym-bin-common to dev-deps

* PR review changes + fix Sink delegation

* Fix borked merge + update README.md

* Fix up stale docs + rewrite examples to use proper imports and timing
logs

* Update readmes + architecture file

* Impl Drop for BridgeShutdownHandle + update comment
2026-04-14 20:13:12 +00:00
dynco-nym 2209e8ac04 Include all gateways in the returned list (#6649)
* Include all gateways in the returned list

* Fix clippy

* Bump API version
2026-04-10 16:55:42 +02:00
import this 6f2a3d9033 [DOCs/operators]: Add -t flag to ansible guides 2026-04-10 14:34:11 +02:00
mfahampshire 0530967807 Fix broken redirects & links. (#6660) 2026-04-10 11:01:20 +00:00
mfahampshire 9db748e8dd Max/sdk docrs (#6566)
* Improve SDK rustdoc and add ARCHITECTURE.md files

- Rewrite lib.rs module docs with quick-start example and module overview
- Add stream example and include_str! ARCHITECTURE.md to mixnet module
- Add ARCHITECTURE.md for mixnet, client_pool, and stream modules
- Add rustdoc to MixnetClientBuilder, MixnetClientSender, MixnetMessageSender
- Add cancel safety and drop behavior annotations to async methods
- Add TcpProxy deprecation notice pointing to stream module

* Fix rustdoc errors and add stepwise comments to remaining examples

Rustdoc fixes:
- Add missing .unwrap() on connect_new example
- Replace broken turbofish intra-doc link in MixnetClientBuilder
- Fix NymProxyServer::new args in tcp_proxy example
- Wrap BandwidthImporter example in scoped block to fix borrow-then-move
- Change misleading "5-hop routing" to "multi-hop routing"
- Fix copy-paste "forget me" in send_remember_me error message
- Fix wrong cargo run command in stream_simple_read_write
- Fix DecayWrapper description

* Cut down doc comment length

* Trimmed down SDK ARCHITECTURE files

* Slim Rust SDK docs and rename opener to dialer

- Merge tour page into SDK landing page, delete tour.mdx
- Trim all three tutorials: cut boilerplate, duplicated code, and misplaced content
- Make FFI page evergreen with Go and C++ snippets, link to repo examples
- Rename "opener" to "dialer" in stream docs, source ARCHITECTURE.md, and rustdoc
- Add reply-to-open arrow in stream mermaid diagram
- Replace remaining Unicode dashes in mermaid flowchart

* - elevate streams in rustdoc: examples on lib.rs, MixnetClient, open_stream, listener
- add stream quick reference to mixnet ARCHITECTURE.md
- add stream types to key types list in ARCHITECTURE.md
- add docs.rs links for AsyncRead/AsyncWrite and stream submodule
- tcp_proxy: replace bold deprecation with warning box

* - replace individual example doc pages with GitHub-linked tables
- add step-by-step inline comments to all SDK example source files
- add doc comments to examples missing them (simple, surb_reply, builder, etc.)
- expand mixnet tutorial with persistent identity and split_sender sections
- add tcpproxy tutorial
- rename "API Reference" to "TypeDoc Reference" in TS SDK sidebar
- rename "Misc" to "Extras" in developer sidebar, move VPN CLI up
- remove echo server from tools
- update message-queue callout to reference actual modules
- fix mixnet/examples redirect collision

* Add missing mut to example code

* Update ARCHITECTURE.md with LP Framing + stream examples with sequencing

* Update doc comment in utils.rs

* Standardise commenting style across Rust SDK examples

* Fix inline doc examples and trim re-export boilerplate

* Update sdk/rust/nym-sdk/examples/bandwidth.rs

Co-authored-by: Simon Wicky <simon@nymtech.net>

* Fix review comments

---------

Co-authored-by: Simon Wicky <simon@nymtech.net>
2026-04-10 10:51:38 +00:00
mfahampshire 82ed88e26e Update revs for all tutorials to current release & tweak tutorial (#6659)
* Update revs for all tutorials to current release.

* Update missed rev

* Bump sizes of sent echo messages
2026-04-10 08:01:42 +00:00
mfahampshire 594174827d Minor grammar tweaks (#6658)
* Minor grammar tweaks

* Minor final tweaks to grammar.
2026-04-09 16:55:12 +00:00
import this f6f364c551 Operators: Ansible version syntax and comment update (#6657) 2026-04-09 17:39:41 +02:00
mfahampshire f648349e82 Max/docs-diataxis-ify (#6494)
* Diatixisify!

* First pass at Typedoc generation for TS SDK

* Remove overview pages

* Fix typos and remove codebase references from docs

Fix typos across network and developer docs: Quorum, available,
cryptosystem, transaction, proportional, Standalone. Remove TODO
placeholder from dVPN protocol page. Strip GitHub source links
from network docs to decouple documentation from repo structure.

* Expand thin landing pages across network and developer docs

- Add intro content to network overview, infrastructure, and reference landing pages
- Expand developer index with "where to start" guide
- Add usage instructions and explanations to all five TS playground pages
- Expand WebSocket client page with setup and message format examples

* Restructure Rust SDK developer docs

- Delete redundant mixnet example, message-helpers, and message-types subpages
- Delete client-pool architecture and example subpages (content folded into landing)
- Delete tcpproxy troubleshooting (folded into landing page)
- Add deprecation notices to TcpProxy pages, pointing to Stream module
- Add stream module docs: landing page, architecture, tutorial, and 4 example pages
- Add mixnet and client-pool tutorials
- Add SDK tour page
- Update navigation and landing pages with docs.rs links

* Restructure TS SDK developer docs

- Merge overview, installation, and getting started into TS SDK landing page
- Fold FAQ content into bundling/troubleshooting section
- Delete redundant overview, installation, start, and FAQ pages
- Update internal links in browsers.mdx and native.mdx
- Update navigation and example page imports

* Flatten and expand APIs section

- Collapse nested API subpages into single pages with inline Redoc embeds
- Rewrite introduction as landing page with decision table
- Add endpoint categories, quick curl examples to each API page
- Mark Explorer API as deprecated
- Move NS API deployment guide to operators/performance-and-testing
- Fix dangling /apis/nym-api/mainnet link in network-components
- Remove sandbox endpoints from all API pages

* Add redirects for moved and deleted pages

- Add 25 redirects covering TS SDK, Rust SDK, APIs, and network sections
- Fix dangling /developers/typescript/start link in operators changelog

* Replace individual example doc pages with GitHub-linked tables, expand tutorials

- replace individual example doc pages with GitHub-linked tables
- expand mixnet tutorial with persistent identity and split_sender sections
- add tcpproxy tutorial
- rename "API Reference" to "TypeDoc Reference" in TS SDK sidebar
- rename "Misc" to "Extras" in developer sidebar, move VPN CLI up
- remove echo server from tools
- update message-queue callout to reference actual modules
- fix mixnet/examples redirect collision

* Add SEO frontmatter, validate encryption standards, clean up URLs

- add title/description/schemaType/section/lastUpdated frontmatter to 48
  pages across developers, network, and APIs sections
- remove network/.archive/ directory (compare against develop instead)
- update nymtech.net → nym.com for website/blog links (keep infra URLs)
- add native proxy "in progress" callout for Rust/C/Go

* API-scraper update (#6598)

* read nodes and locations

* update python-prebuild.sh

* Address PR #6494 review feedback
- Use "mode" consistently instead of "role" on nym-nodes page
- Replace "staking" with "bonding" for NYM token collateral
- Wire up auto-scraped node counts via TimeNow + nodes-count.json
- Fix broken licensing images: download CC icons locally, replace inline HTML
- Fix 9 stale redirects pointing through deleted /network/architecture path

* Fix linkcheck errors
- Fix stale cross-links: /network/concepts/ → /network/mixnet-mode/
- Replace README.md references with globals.md in TypeDoc output
- Add entryFileName: globals to typedoc.json configs to prevent recurrence

* Fix remaining stale /network/architecture links
- zk-nym-overview: architecture/nyx#nym-api → /network/infrastructure/nyx#nym-api
- setup: network/architecture → /network/overview

* Remove accidentally re-included architecture.md file from rebase

* Standardize tutorials, document examples, add llms.txt, apply tone fixes

- Expand Rust SDK tutorials with step-by-step structure; document all SDK examples across mixnet, client-pool, and tcpproxy pages
- Add llms.txt generation script, wire into build and CI workflows
- Apply tone/style fixes: deduplicate callouts, vary sentence structure, standardize voice consistency across changed pages

* Consolidate redundant network overview docs

* Trim dev docs: git-first imports, stream notice, collapse TcpProxy

* Update tutorial

* Refresh auto-generated API and command outputs

* Update network section docs

* Update developer and API docs: reusable components, stream protocol, conventions, tutorial fixes

* Fix Rust SDK tutorial bugs: setup_env, port conflicts, logging,
open_stream race condition

* Update stream.mdx

* Remove docs.rs link from Stream overview for the moment

* add llms.txt and llms-full.txt note to readme

---------

Co-authored-by: import this <97586125+serinko@users.noreply.github.com>
2026-04-09 15:25:31 +00:00
import this 4fb78c3737 FIX: add tags to Ansible NTM role (#6656) 2026-04-09 17:15:57 +02:00
Merve f208855bc8 [DOCs/operators]: Release notes for tola release (#6645)
* changelog for tola release

* Add new updates to changelog

* add docs rework to operatos news

* add ansible fix

* bump stats

---------

Co-authored-by: merve <e@E-MacBook-Air.local>
Co-authored-by: serinko <97586125+serinko@users.noreply.github.com>
2026-04-09 15:06:52 +02:00
import this 60426b8c45 NTM: NIP-10 exit policy upgrade (#6648) 2026-04-09 14:41:02 +02:00
benedetta davico 9792a8829b Merge pull request #6646 from nymtech/release/2026.7-tola
merge release/2026.7-tola
2026-04-09 14:18:16 +02:00
import this 5b23429415 Bugfix: Ansible dwl fresh NTM on each run (#6654) 2026-04-09 13:53:08 +02:00
dynco-nym 89de989ad1 Optimize GW probe in NS agent (#6636)
* WIP

* NS agent calls probe as lib

* Clippy: ns agent

* Fix submit_v2 on API

* Adjust dockerfile, deployment details for the new flow

* Bump package versions

* PR feedback

* Fix CI

* Final version
2026-04-07 16:35:50 +02:00
benedettadavico 97068b2aac update changelog 2026-04-07 15:51:44 +02:00
benedetta davico 0e3e5c27f3 Merge pull request #6634 from nymtech/simon/ecash-contract-serde-fix
Simon/ecash contract serde fix
2026-04-01 10:56:27 +02:00
Simon Wicky 01e3c8206b alias not working, adding separate method 2026-03-31 17:32:57 +02:00
Simon Wicky ef20b8c7d1 serde magic on ecash contract 2026-03-31 14:39:34 +02:00
benedetta davico 61af16784b Merge pull request #6632 from nymtech/bdq/ecash-contract-test
small fix to allow ecash migrate
2026-03-30 13:33:34 +02:00
benedettadavico caf21076c9 .. 2026-03-30 10:37:45 +02:00
benedettadavico 1672135308 bump versions 2026-03-30 07:11:55 +02:00
mfahampshire c07ef0253d Max/sdk stream wrapper (#6320)
* Replace MixnetStream with LP framing
- Replace custom header with LpFrameHeader
- Added sequence number for message ordering

* IPR: support LP Stream-framed client connections
- Detect and route LP Stream frames in mixnet_listener
- Wrap inline responses in LP Stream frames
- Thread stream_id to ConnectedClientHandler for TUN responses

* sdk: add ipr_wrapper module with IpMixStream
- IpMixStream wraps MixnetStream for IPR tunnel over mixnet
- LP Stream framing handled automatically by MixnetStream
- Gateway discovery, connect handshake, IP packet send/receive

* sdk: remove superseded stream_wrapper module

* Trim obvious comments, add architecture.md stub

* sdk: add missing deps and fix warnings

* Cut down architecture diagram until finished with rest of the code, leaving stubs

* sdk: refactor IpMixStream, extract shared helpers

- Extract gateway discovery and connect response parsing
- Add recv() to MixnetStream, remove 64KB read buffer
- Simplify IpMixStream constructor

* Fix SphinxStream renames missed during rebase

* Add IpPacketResponse::from_bytes() for stream-based deserialization

* Clean up ip_packet_client: delete stale connect.rs, take raw bytes not ReconstructedMessage

* Clippy

* Delete unused ip_packet_client modules

- Remove helpers.rs (ICMP utilities moved to example)
- Remove error.rs (errors consolidated into sdk/error.rs)
- Remove README.md
- Update module root to only export discovery + listener

* Simplify listener, IpMixStream, and network_env

- Collapse IprListener struct into standalone handle_ipr_response()
- Move check_ipr_message_version() into listener.rs
- Remove IpMixStream test module (moved to example)
- Remove parse_network() and commented-out Sandbox arms
- Return Result from find_workspace_root() instead of panicking
- Add IprTunnelDisconnected and WorkspaceRootNotFound error variants

* Refactor IPR stream handling and document seq conventions
- Inline stream_id tracking (remove current_stream_id field)
- Re-export encode_stream_frame from clients module
- Document seq=0 reservation for inline control responses
- Document data-path counter starting at 1 with skip-on-wrap

* Add ipr_tunnel example for integration testing
- ICMP ping through IPR with --gateway flag for targeting specific exits
- Move pnet_packet from dependencies to dev-dependencies

* Add message reordering to stream router
- Buffer out-of-order messages per-stream using BTreeMap
- Drain contiguous sequences individually to preserve message boundaries
- Drop duplicate/old sequence numbers with a warning
- Remove dead_code allow on StreamFrame::sequence_num

* Clean up comments and fill architecture.md
- Remove separator line comments
- Update stale comments about ordering not being implemented
- Remove collapsible_if allows, use let-else instead
- Fill in architecture.md data flow and connection lifecycle

* Simplify ipr_tunnel example to minimal smoke test
- Single ping instead of multi-ping loop
- Remove identifier and PING_COUNT
- Collapse ICMP helpers into single build_icmp_ping function

* Add dual-stack IPv6 ping and rename gateway → ipr
- Rename --gateway flag to --ipr and new_with_gateway() to new_with_ipr()
- Add ICMPv6 ping to ipr_tunnel example for dual-stack smoke test
- Tighten echo reply validation (protocol field check, diagnostic output)
- Document IP allocation (subnets, static vs dynamic, client keying) in architecture.md
- Promote LP Stream Open handshake log to INFO

* Tweak subnet comment in docs

* Don't stop IPR listener on decode failure
- Change break to continue so garbage packets can't kill the listener
- Remaining valid packets in the bundle are still processed

* Fix license headers and use workspace dep for pnet_packet
- Switch GPL-3.0 to Apache-2.0 on all SDK library files
- Add missing license headers to 7 files
- Use workspace version for pnet_packet dependency

* Document IP pool isolation from WG/LP dVPN pool
- IPR uses 10.0.0.0/16 on nymtun, WG uses 10.1.0.0/16 on nymwg
- Reference constants.rs as source of truth

* Remove network_env.rs and simplify IpMixStream API
  - Default to mainnet via setup_env(None) instead of requiring env param
  - Remove NetworkEnvironment enum and workspace root detection
  - Remove WorkspaceRootNotFound error variant
  - Update ipr_tunnel example to match new signatures

* Use weighted random selection for IPR gateway discovery
  - Replace max_by_key with choose_weighted biased by performance score
  - Prevents all clients converging on a single highest-performing IPR

* Cap stream reorder buffer to prevent unbounded memory growth
- Add MAX_REORDER_BUFFER (256) to limit per-stream pending messages:
	- buffer overflows = skip ahead to lowest buffered seq and drain
	- protects against malicious senders that deliberately skip sequence numbers

* Extract shared IPR response helpers into nym-ip-packet-requests
  - Add response_helpers module with version check, connect response
    parsing, and control response dispatch
  - SDK ip_packet_client now delegates to shared module
  - Monorepo nym-ip-packet-client uses shared version check and
    connect response parsing
  - Fix doc comment attributing fork to nym-vpn-client

* Extract ICMP test helpers into nym-ip-packet-requests
  - Add icmp_utils module behind test-utils feature flag
  - Move build_icmp_ping, build_icmpv6_ping, is_echo_reply_v4/v6 from
    example
  - Update ipr_tunnel example to use shared helpers

* Add protocol v9 LP-framed transport marker

- Add v9 module (re-exports v8, VERSION=9)
- Accept v9 requests and responses in IPR
- Switch SDK IpMixStream to send v9

* Log protocol version in dynamic connect requests

* Remove KCP from IPR and fix unwrap_or_default in SDK
- Remove all KCP session management from ip-packet-router (replaced by
  LP Stream framing)
- Drop nym-kcp dependency and KcpError variant from IPR
- Replace unwrap_or_default with ok_or(Error::NoNymAPIUrl) in
  IpMixStream::new()

* Add v9 protocol wrapper constructors and enforce version/transport
consistency
- Add v9::new_connect_request(), new_data_request(),
  new_ip_packet_response() to centralise version stamping
- Replace manual protocol.version overrides in SDK and IPR with v9
  wrapper calls
- Bump nym-ip-packet-client current re-export from v8 to v9
- Enforce LP Stream frames must carry v9+ payloads, non-stream must be
  v8 or lower

* Filter IPR exit nodes by minimum v9-compatible release version
- Define MIN_RELEASE_VERSION (1.30.0) in ip-packet-requests/v9 alongside protocol constants
- Add semver-based filtering in SDK gateway discovery to skip nodes below v9 threshold
- Add semver dependency to ip-packet-requests and nym-sdk

* Use numeric version comparison for transport/version enforcement
- Compare version as u8 instead of enum equality so future v10+ is handled correctly
- Remove unused `use super::*` import left over from KCP test removal
2026-03-27 20:35:26 +00:00
benedetta davico cc799b69d3 Merge pull request #6622 from nymtech/jmwample/fallback-nym-ip
Update Fallback IP for Nym API
2026-03-27 10:06:13 +01:00
jmwample dd4bbc0708 nym-api moved default 2026-03-26 11:36:04 -06:00
Jack Wampler 7b77091fb1 Nym Node spam logging (#6621)
prevent spam logs when downstream node is slow
2026-03-26 11:27:14 -06:00
Jędrzej Stuczyński 6581ebf235 feat: multiple deposit prices (#6608)
* added reduced pricing handling logic

* admin methods for setting the whitelist of reduced price accounts

* updated client traits

* query to get all whitelisted accounts

* query for getting detailed deposit statistics

* fixes

* set initial whitelisted accounts in the migration

* stop transferring tokens to the holding account after redemption

* stop gateways from creating redemption multisig proposals

* make sure credential-proxy uses reduced deposits when available

* cargo fmt

* update deposit handler to allow EITHER default price or reduced price

this will allow non-breaking upgrades of NS and credential proxy

* removed use of unstable rust features

* rebuilt contract schema

* correct license timestamp
2026-03-26 16:02:19 +00:00
benedetta davico 82ace6d27b Merge pull request #6611 from nymtech/master
Keep master and develop in sync
2026-03-26 16:07:36 +01:00
import this e362207583 [DOCs/operators]: Fix - disable ufw to clean machine conf state (#6620) 2026-03-26 12:27:57 +01:00
import this 68caecff35 [DOCs]: Release notes v2026.6 stilton (#6606)
* operators updates

* add headers

* Update changelog.mdx

* bump up node version

* udpate time

* edit typos

---------

Co-authored-by: Merve <111695676+merve64@users.noreply.github.com>
2026-03-26 11:02:10 +01:00
import this 2fae4414d2 NTM Update: single port managment tool (#6607)
* update ntm

* update docs

* add table for ports

* cherry on the cake

* polish ntm

* quic cherry - add 4443
2026-03-26 10:18:32 +01:00
benedetta davico 6eca09b904 Merge pull request #6610 from nymtech/release/2026.6-stilton
Merge stilton to master
2026-03-25 17:09:28 +01:00
benedetta davico 7ab821cb11 Merge pull request #6609 from nymtech/release/2026.6-stilton
Merge stilton to develop
2026-03-25 17:09:16 +01:00
benedettadavico 0343469179 update changelog 2026-03-25 07:47:04 +01:00
mfahampshire 9904f6b17c Make mobile friendly (#6605)
- Add overflow:hidden on grid
- Shrink `pre` font on mobile
- Stack grid on narrow pages
2026-03-24 21:56:15 +00:00
mfahampshire 5e0eeeddd6 hotfix (#6603) 2026-03-24 15:32:30 +00:00
mfahampshire b6df383584 Max/docs theme rework (#6593)
* Rawer landing page
- Angular, clean docs styling inspired by Oxide
- zero all border-radius globally (kill rounded corners)
- sharp code blocks with subtle border
- callouts: left-border accent instead of rounded pill
- clean table grid lines, sharp search box and MUI buttons
- tighter heading letter-spacing (-0.02em)
- flat left-border sidebar active item instead of background blob

* Add JetBrains Mono for headings/sidebar, push Oxide styling further
- import JetBrains Mono via Google Fonts
- apply mono font to headings, sidebar, nav bar, search, table headers
- darken background (#181C1E), muted body text, h2 bottom border
- subtle background tint on active sidebar item
- inline code: background-only (no border), monospace table headers
- fix active sidebar item font size (scope separator label rule)

* Rework docs landing page: hero, ASCII cards, SDKs, get started
- add hero section with subtitle covering all doc areas
- replace PNG vector illustrations with ASCII art in primary green
- add SDKs section with Rust and TypeScript links
- add get started section: What is the Mixnet, Send a message, Run a node
- add footer links to GitHub and Matrix
- fix nav dropdown font (button + ul selectors)
- add landing card hover style

* Self-host JetBrains Mono, refine landing page
- replace Google Fonts import with local @font-face (woff2)
- add font files + OFL license to public/fonts/
- remove redundant "Nym Docs" hero heading (already in nav)
- drop quick-links pills section
- fix SDK box borders (negative margin collapse)
- rewrite footer as simple link row (GitHub, Matrix, nym.com)

* Light mode styling, dark-mode diagram invert, click-to-expand images
- add full light mode CSS: pale grey bg, darker green links, mono fonts
- invert diagram images in dark mode with mix-blend-mode: lighten
- add click-to-expand overlay for content images
- revert mermaid diagrams back to original PNGs

* Fix Lychee config for fonts

* Make light mode green darker

* Animate landing page ASCII art, remove architecture diagram

- Network: animated packet traversal through gw_e → M1/M2/M3 → gw_ex
  with diagonal cross-connections showing mixing paths
- Developers: typewriter effect with blinking cursor
- Operators: looping progress bar with continuously incrementing packet count
- APIs: staged line-by-line response reveal
- Remove architecture overview PNG from network/architecture.mdx

* Small copy change to SDK headers

* Fix links
2026-03-24 15:08:07 +00:00
Simon Wicky b7d13d6fa6 lp fixes (#6601) 2026-03-23 16:18:45 +01:00
benedetta davico 838dd630ae Merge pull request #6590 from nymtech/ci-runner-22.04
temporarily change binaries ci runner to 22.04
2026-03-20 15:38:46 +01:00
benedetta davico 3f00e2c317 Merge pull request #6592 from nymtech/bdq/bump-ns-version
bump NS versions
2026-03-20 15:37:18 +01:00
benedettadavico 3cdda8fdfd bump NS version 2026-03-20 15:33:16 +01:00
benedetta davico 33f47ef36e Merge pull request #6591 from nymtech/release/2026.6-stilton
merge stilton to develop
2026-03-20 15:30:48 +01:00
benedetta davico 7f9dba6e99 Change OS from arc-linux to ubuntu-22.04 2026-03-20 15:24:53 +01:00
benedetta davico 96e88b6ea9 Change CI platform from arc-linux to ubuntu-22.04 2026-03-20 14:42:10 +01:00
dynco-nym 180802feb8 Fix socks5 GW probe regression (#6576)
* Restore tested gateway into topology

* Bump agent version

* Update .sqlx files

* Clean up code in probe test

* Probe error & logging improvements

* Fix clippy, improve log line

* Improve logging in ns agent

* Better tooling in NS API

* Bump agent

* Bump NS agent version
2026-03-20 10:36:32 +01:00
Jędrzej Stuczyński 87882f70cf bugfix: allow deserialisation of LP data from either snake_case or lowercase (#6586) 2026-03-20 08:26:27 +00:00
mfahampshire 4077717d3a Max/lp stream framing (#6573)
* Add LpFrameKind::Stream variant with StreamFrameAttributes
- Define LP wire format for stream multiplexing
- Handle new variant in entry gateway match arm

* Replace MixnetStream with LP framing
- Replace custom header with LpFrameHeader
- Added sequence number for message ordering

* Revert accidental vergen bump

* Revert accidental bumps

* Rename Stream to SphinxStream and split match arms in client_handler

* Add LpFrameAttributes type alias for [u8; 14]
2026-03-19 15:30:59 +00:00
Simon Wicky bc3df31518 move format_debug_bytes in common crate (#6580)
* move format_debug_bytes in common crate

* license change
2026-03-19 15:09:20 +01:00
Jack Wampler 61d6acace8 HTTP domain rotation conditions (#6570)
Add more explicit handling for df enable and domain rotations
2026-03-19 07:38:48 -06:00
Jędrzej Stuczyński abb4e3f988 bugfix: make sure client keys are generated before requesting credentials (#6579) 2026-03-19 08:55:00 +00:00
mfahampshire c5488337da Max/mixfetch docs tweak (#6523)
* update mixfetch concurrency info

* Update MixFetch version + update note

* Update python3 install method on docs runners
2026-03-18 14:23:51 +00:00
mfahampshire f06eefe184 Only publish mixfetch in script (#6560) 2026-03-18 14:01:24 +00:00
benedettadavico 46a8697a5d version bump 2026-03-18 13:17:14 +01:00
Jędrzej Stuczyński 0429238b0f bugfix: make sure to run cargo install cosmwasm-check with --locked flag during CI (#6568) 2026-03-17 14:52:01 +00:00
benedetta davico e86fa8fc7f Merge pull request #6537 from nymtech/release/2026.5-raclette
Raclette to master
2026-03-10 12:07:12 +01:00
717 changed files with 44080 additions and 8294 deletions
+3 -3
View File
@@ -15,10 +15,8 @@ jobs:
- uses: actions/checkout@v6
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
- name: Install pip3
run: sudo apt install -y python3-pip
- name: Install Python3 modules
run: sudo pip3 install pandas tabulate
run: sudo apt install -y python3-pandas python3-tabulate
- name: Install rsync
run: sudo apt-get install -y rsync
- uses: rlespinasse/github-slug-action@v3.x
@@ -41,6 +39,8 @@ jobs:
- name: Install project dependencies
run: pnpm i
- name: Generate llms-full.txt
run: pnpm run generate:llms
- name: Build project
run: pnpm run build
- name: Generate sitemap
@@ -36,7 +36,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [arc-linux-latest]
platform: [ubuntu-22.04]
runs-on: ${{ matrix.platform }}
env:
+5 -4
View File
@@ -90,7 +90,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --exclude nym-gateway-probe --exclude nym-node-status-api -- -D warnings
args: --workspace --all-targets --exclude nym-gateway-probe --exclude nym-node-status-api --exclude nym-node-status-agent --exclude nym-node-status-client -- -D warnings
- name: Clippy (non-macos)
if: contains(matrix.os, 'linux') || contains(matrix.os, 'windows')
@@ -104,14 +104,15 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --exclude nym-gateway-probe --exclude nym-node-status-api --exclude nym-node-status-agent --exclude nym-node-status-client
# only build on linux because of wg FFI bindings of its dependency (network probe)
- name: Build nym-node-status-api (linux only)
# Build Go FFI-dependent crates separately (requires Go, only available on Linux CI)
- name: Build nym-node-status-api and nym-node-status-agent (linux only)
if: runner.os == 'Linux'
uses: actions-rs/cargo@v1
with:
command: build
args: -p nym-node-status-api
args: -p nym-node-status-api -p nym-node-status-agent
- name: Build all examples
if: contains(matrix.os, 'linux')
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
components: rustfmt, clippy
- name: Install cosmwasm-check
run: cargo install cosmwasm-check
run: cargo install cosmwasm-check --locked
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
@@ -15,6 +15,9 @@ env:
jobs:
publish-dry-run:
runs-on: arc-linux-latest
timeout-minutes: 35
env:
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
- name: Checkout repo
uses: actions/checkout@v6
@@ -59,20 +62,60 @@ jobs:
- name: Bump versions (local only)
run: |
cargo workspaces version custom ${{ inputs.version }} \
--allow-branch ${{ github.ref_name }} \
--no-git-commit \
--yes
- name: Preflight publish checks
run: |
python3 tools/internal/check_publish_preflight.py
# Dry run may show cascading dependency errors because packages aren't
# actually uploaded - these are expected and ignored. We check for real
# errors like packaging failures, missing metadata, or invalid Cargo.toml.
- name: Publish (dry run)
run: |
output=$(cargo workspaces publish --dry-run --allow-dirty 2>&1) || true
echo "$output"
set +e
publish_status=1
max_attempts=2
attempt=1
rm -f /tmp/publish-dry-run.log
# Check for real errors (not cascading dependency errors)
# Cascading errors mention "crates.io index", real errors mention "Cargo.toml"
echo "$output" | grep -i "Cargo.toml" && exit 1 || true
while [ "$attempt" -le "$max_attempts" ]; do
echo "Dry-run publish attempt ${attempt}/${max_attempts}"
cargo workspaces publish --dry-run --allow-dirty 2>&1 | tee /tmp/publish-dry-run.log
publish_status=${PIPESTATUS[0]}
if [ "$publish_status" -eq 0 ]; then
break
fi
# Retry once for interruption/runner issues.
if [ "$attempt" -lt "$max_attempts" ] && \
{ [ "$publish_status" -eq 130 ] || [ "$publish_status" -eq 137 ]; }; then
echo "Publish dry-run interrupted (exit ${publish_status}), retrying in 10s..."
sleep 10
attempt=$((attempt + 1))
continue
fi
break
done
set -e
if grep -Eiq \
"failed to verify manifest|failed to parse manifest|invalid Cargo.toml|error: package .* has no (description|license|repository)" \
/tmp/publish-dry-run.log; then
echo "Detected real packaging/manifest errors"
exit 1
fi
# In dry-run mode, non-zero publish status is expected due to
# dependency-cascade failures against crates.io index.
if [ "$publish_status" -ne 0 ]; then
echo "Dry-run publish returned non-zero (${publish_status}) but no real manifest blockers were detected."
fi
echo "Only expected dry-run dependency cascade errors detected (if any)."
# Show the list of packages published
- name: Show package versions
@@ -17,6 +17,8 @@ on:
jobs:
publish:
runs-on: arc-linux-latest
env:
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
- name: Checkout repo
uses: actions/checkout@v6
+2
View File
@@ -17,6 +17,8 @@ on:
jobs:
publish:
runs-on: arc-linux-latest
env:
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
- name: Checkout repo
uses: actions/checkout@v6
@@ -15,6 +15,8 @@ env:
jobs:
version-bump:
runs-on: arc-linux-latest
env:
RUSTUP_PERMIT_COPY_RENAME: 1
permissions:
contents: write
steps:
+22 -3
View File
@@ -6,6 +6,8 @@ on:
branches-ignore: [master]
paths:
- "documentation/docs/**"
- "sdk/typescript/packages/sdk/src/**"
- "sdk/typescript/packages/mix-fetch/src/**"
- ".github/workflows/ci-docs.yml"
jobs:
@@ -20,10 +22,8 @@ jobs:
- uses: actions/checkout@v6
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
- name: Install pip3
run: sudo apt install -y python3-pip
- name: Install Python3 modules
run: sudo pip3 install pandas tabulate
run: sudo apt install -y python3-pandas python3-tabulate
- name: Install rsync
run: sudo apt-get install -y rsync
- uses: rlespinasse/github-slug-action@v3.x
@@ -44,8 +44,27 @@ jobs:
command: build
args: --workspace --release
- name: Check if TypeScript SDK source changed
id: check-ts-sdk
run: |
if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -qE '^sdk/typescript/packages/(sdk|mix-fetch)/src/'; then
echo "changed=true" >> $GITHUB_OUTPUT
else
echo "changed=false" >> $GITHUB_OUTPUT
fi
working-directory: ${{ github.workspace }}
- name: Regenerate TypeDoc API reference
if: steps.check-ts-sdk.outputs.changed == 'true'
run: |
npm install -g typedoc@0.25.13 typedoc-plugin-markdown@4.0.3
cd ${{ github.workspace }}/sdk/typescript/packages/sdk && typedoc --skipErrorChecking
cd ${{ github.workspace }}/sdk/typescript/packages/mix-fetch && typedoc --skipErrorChecking
- name: Install project dependencies
run: pnpm i
- name: Generate llms-full.txt
run: pnpm run generate:llms
- name: Build project
run: pnpm run build
- name: Generate sitemap
+24
View File
@@ -54,6 +54,30 @@ jobs:
- name: "Build"
run: make sdk-wasm-build
- name: "Build mix-fetch WASM (debug)"
run: |
make -C wasm/mix-fetch/go-mix-conn build-debug-dev
make -C wasm/mix-fetch build-rust-debug
- name: "Build mix-fetch internal-dev harness"
working-directory: wasm/mix-fetch/internal-dev
run: npm install && npm run build
- name: "Install Playwright browsers"
working-directory: wasm/mix-fetch/tests
run: npm install && npx playwright install --with-deps # --with-deps assumes Ubuntu/Debian, see note in wasm/mix-fetch/tests/README.md
- name: "Smoke-test mix-fetch internal-dev (headless)"
working-directory: wasm/mix-fetch/tests
run: npm run test:smoke
- name: Upload Playwright traces on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: mix-fetch-playwright-traces
path: wasm/mix-fetch/tests/test-results/
- name: "Test"
run: make sdk-wasm-test
@@ -0,0 +1,61 @@
name: nightly-mix-fetch-stress
on:
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
jobs:
stress:
runs-on: arc-linux-latest
env:
CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ vars.REQUIRED_RUSTC_VERSION }}
target: wasm32-unknown-unknown
override: true
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24.6"
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli
- name: "Build mix-fetch WASM (debug)"
run: |
make -C wasm/mix-fetch/go-mix-conn build-debug-dev
make -C wasm/mix-fetch build-rust-debug
- name: "Build internal-dev harness"
working-directory: wasm/mix-fetch/internal-dev
run: npm install && npm run build
- name: "Install Playwright browsers"
working-directory: wasm/mix-fetch/tests
run: npm install && npx playwright install --with-deps
- name: "Stress-test mix-fetch through mainnet"
working-directory: wasm/mix-fetch/tests
run: npm run test:stress
- name: Upload Playwright traces on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: mix-fetch-stress-traces
path: wasm/mix-fetch/tests/test-results/
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
fail-fast: false
matrix:
include:
- os: arc-linux-latest
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
@@ -25,6 +25,10 @@ jobs:
- name: Install cargo-workspaces
run: cargo install cargo-workspaces
- name: Preflight publish checks
run: |
python3 tools/internal/check_publish_preflight.py
- name: Publish remaining crates
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
+70
View File
@@ -4,6 +4,76 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2026.7-tola] (2026-04-07)
- Simon/ecash contract serde fix ([#6634])
- Update Fallback IP for Nym API ([#6622])
- Nym Node spam logging ([#6621])
- feat: multiple deposit prices ([#6608])
- move format_debug_bytes in common crate ([#6580])
- bugfix: make sure client keys are generated before requesting credentials ([#6579])
- Fix socks5 GW probe regression ([#6576])
- Max/lp stream framing ([#6573])
- HTTP domain rotation conditions ([#6570])
[#6634]: https://github.com/nymtech/nym/pull/6634
[#6622]: https://github.com/nymtech/nym/pull/6622
[#6621]: https://github.com/nymtech/nym/pull/6621
[#6608]: https://github.com/nymtech/nym/pull/6608
[#6580]: https://github.com/nymtech/nym/pull/6580
[#6579]: https://github.com/nymtech/nym/pull/6579
[#6576]: https://github.com/nymtech/nym/pull/6576
[#6573]: https://github.com/nymtech/nym/pull/6573
[#6570]: https://github.com/nymtech/nym/pull/6570
## [2026.6-stilton] (2026-03-25)
- lp fixes ([#6601])
- bugfix: allow deserialisation of LP data from either snake_case or lowercase ([#6586])
- bugfix: make sure to run cargo install cosmwasm-check with --locked flag during CI ([#6568])
- Add LP to NS UI ([#6562])
- feat: nyxd watcher ([#6561])
- Additional ticket for agent ([#6551])
- bugfix: make sure to use old values from metrics debug config during v12 migration (#6546) ([#6547])
- typo ([#6543])
- rng changes for a Send variant ([#6541])
- Add LP fields ([#6535])
- enable LP registration in registration client ([#6534])
- chore: rename LpMessage to LpFrame ([#6530])
- chore: LP improvements ([#6526])
- Remove dep leak of strum iterator ([#6522])
- chore: update ts-rs dep ([#6517])
- addressing LP PR comments ([#6513])
- remove redundant LP state machine in favour of in place processing ([#6512])
- chore: split up lp listener ([#6507])
- feat: enable mutual KKT exchange ([#6505])
- feat: introduce /v3/unstable/nym-nodes/semi-skimmed to aggregate LP information ([#6499])
- Max/asyncread asyncwrite nym client ([#6318])
- feat: localnet v2 ([#6277])
[#6601]: https://github.com/nymtech/nym/pull/6601
[#6586]: https://github.com/nymtech/nym/pull/6586
[#6568]: https://github.com/nymtech/nym/pull/6568
[#6562]: https://github.com/nymtech/nym/pull/6562
[#6561]: https://github.com/nymtech/nym/pull/6561
[#6551]: https://github.com/nymtech/nym/pull/6551
[#6547]: https://github.com/nymtech/nym/pull/6547
[#6543]: https://github.com/nymtech/nym/pull/6543
[#6541]: https://github.com/nymtech/nym/pull/6541
[#6535]: https://github.com/nymtech/nym/pull/6535
[#6534]: https://github.com/nymtech/nym/pull/6534
[#6530]: https://github.com/nymtech/nym/pull/6530
[#6526]: https://github.com/nymtech/nym/pull/6526
[#6522]: https://github.com/nymtech/nym/pull/6522
[#6517]: https://github.com/nymtech/nym/pull/6517
[#6513]: https://github.com/nymtech/nym/pull/6513
[#6512]: https://github.com/nymtech/nym/pull/6512
[#6507]: https://github.com/nymtech/nym/pull/6507
[#6505]: https://github.com/nymtech/nym/pull/6505
[#6499]: https://github.com/nymtech/nym/pull/6499
[#6318]: https://github.com/nymtech/nym/pull/6318
[#6277]: https://github.com/nymtech/nym/pull/6277
## [2026.5-raclette] (2026-03-10)
- bugfix: correctly populate gateway probe LP data ([#6533])
Generated
+219 -64
View File
@@ -1691,7 +1691,8 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core-models"
version = "0.0.5"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "657f625ff361906f779745d08375ae3cc9fef87a35fba5f22874cf773010daf4"
dependencies = [
"hax-lib",
"pastey 0.2.1",
@@ -2350,6 +2351,47 @@ dependencies = [
"x25519-dalek",
]
[[package]]
name = "defmt"
version = "0.3.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
dependencies = [
"defmt 1.0.1",
]
[[package]]
name = "defmt"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
dependencies = [
"bitflags 1.3.2",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
dependencies = [
"defmt-parser",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror 2.0.12",
]
[[package]]
name = "delegate-display"
version = "3.0.0"
@@ -2534,7 +2576,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@@ -3398,6 +3440,15 @@ dependencies = [
"serde_json",
]
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -3523,6 +3574,16 @@ dependencies = [
"http 1.3.1",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "heck"
version = "0.4.1"
@@ -3589,7 +3650,7 @@ dependencies = [
"once_cell",
"rand 0.9.2",
"ring",
"rustls 0.23.29",
"rustls 0.23.37",
"thiserror 2.0.12",
"tinyvec",
"tokio",
@@ -3614,7 +3675,7 @@ dependencies = [
"parking_lot",
"rand 0.9.2",
"resolv-conf",
"rustls 0.23.29",
"rustls 0.23.37",
"smallvec",
"thiserror 2.0.12",
"tokio",
@@ -3871,7 +3932,7 @@ dependencies = [
"http 1.3.1",
"hyper 1.6.0",
"hyper-util",
"rustls 0.23.29",
"rustls 0.23.37",
"rustls-native-certs 0.8.3",
"rustls-pki-types",
"tokio",
@@ -4611,7 +4672,8 @@ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "libcrux-aesgcm"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99f2a019dab4097585a7d4f5b9deebe46cd1e628b16a5bc4cb0ce35e1da334e6"
dependencies = [
"libcrux-intrinsics",
"libcrux-platform",
@@ -4621,8 +4683,9 @@ dependencies = [
[[package]]
name = "libcrux-chacha20poly1305"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc08d044676af21343b32b988411fa98dbb5cf65a03c9df478ced221bbdfdb1b"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4634,7 +4697,8 @@ dependencies = [
[[package]]
name = "libcrux-curve25519"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1e5fd8476a6ed609d24ef42aee5ab6f99f7c65d054f92412da9f499e423299"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4645,7 +4709,8 @@ dependencies = [
[[package]]
name = "libcrux-ecdh"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b65f73ce79337c762eb38bbac91e4c9b9e60cf318e8501b812750c640814d45e"
dependencies = [
"libcrux-curve25519",
"libcrux-p256",
@@ -4655,8 +4720,9 @@ dependencies = [
[[package]]
name = "libcrux-ed25519"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835919315b7042fe9e03b6458efe0db94bf2aa7b873934dbee5b5463a8124b43"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4668,7 +4734,8 @@ dependencies = [
[[package]]
name = "libcrux-hacl-rs"
version = "0.0.4"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2637dc87d158e1f1b550fd9b226443e84153fded4de69028d897b534d16d22e6"
dependencies = [
"libcrux-macros",
]
@@ -4676,7 +4743,8 @@ dependencies = [
[[package]]
name = "libcrux-hkdf"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1a89ca0c89be3a268a921e47105fb7873badf7267f5e3ebf4ea46baedd73ef"
dependencies = [
"libcrux-hacl-rs",
"libcrux-hmac",
@@ -4686,7 +4754,8 @@ dependencies = [
[[package]]
name = "libcrux-hmac"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7a242707d65960770bd7e14e4f18a92bdf0b967777dd404887db8d087a643b"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4696,7 +4765,8 @@ dependencies = [
[[package]]
name = "libcrux-intrinsics"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1b5db005ff8001e026b73a6842ee81bbef8ec5ff0e1915a67ae65fd2a9fafa5"
dependencies = [
"core-models",
"hax-lib",
@@ -4704,8 +4774,9 @@ dependencies = [
[[package]]
name = "libcrux-kem"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12631592f491d22fd1a176d32b2c6edfb673998fd3987e9d95f8fa79ad2a737b"
dependencies = [
"libcrux-curve25519",
"libcrux-ecdh",
@@ -4720,7 +4791,8 @@ dependencies = [
[[package]]
name = "libcrux-macros"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffd6aa2dcd5be681662001b81d493f1569c6d49a32361f470b0c955465cd0338"
dependencies = [
"quote",
"syn 2.0.106",
@@ -4728,8 +4800,9 @@ dependencies = [
[[package]]
name = "libcrux-ml-dsa"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a72929ed421cc3bf16a946b3e7d2a58d215b0b5c2a12be26b53629f081bf49b2"
dependencies = [
"core-models",
"hax-lib",
@@ -4742,8 +4815,9 @@ dependencies = [
[[package]]
name = "libcrux-ml-kem"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a14ab3e477de9df6ee1273a114018ff62c4996ca9220070c4e5cb1743f94a67d"
dependencies = [
"hax-lib",
"libcrux-intrinsics",
@@ -4758,7 +4832,8 @@ dependencies = [
[[package]]
name = "libcrux-p256"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4778ba25cb08bb8a96bd100e19ed9aecf78337198fd176036e21042b2dd99bc"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4770,15 +4845,17 @@ dependencies = [
[[package]]
name = "libcrux-platform"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d9e21d7ed31a92ac539bd69a8c970b183ee883872d2d19ce27036e24cb8ecc4"
dependencies = [
"libc",
]
[[package]]
name = "libcrux-poly1305"
version = "0.0.4"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02491808ee5b9db8cb65fad64ae0be812db64beef179d945c00c7787dc7dfcf9"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4786,8 +4863,9 @@ dependencies = [
[[package]]
name = "libcrux-psq"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779ade7aa5e1b4b400c716b313cbf69070988dd005f92e961c2da4c3c42fbea4"
dependencies = [
"classic-mceliece-rust",
"libcrux-aesgcm",
@@ -4809,7 +4887,8 @@ dependencies = [
[[package]]
name = "libcrux-secrets"
version = "0.0.5"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce650f3041b44ba40d4263852347d007cd2cd9d1cc856a6f6c8b2e10c3fd40b"
dependencies = [
"hax-lib",
]
@@ -4817,7 +4896,8 @@ dependencies = [
[[package]]
name = "libcrux-sha2"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9d253473f259fc74a280c43f29c464f7e374abdf28b4942234dc707f529d4b7"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4826,8 +4906,9 @@ dependencies = [
[[package]]
name = "libcrux-sha3"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1ae0b7d0e1cc4793a609fd0ff2ca3b3a3fabae523770c619a3d4bc86417b0d7"
dependencies = [
"hax-lib",
"libcrux-intrinsics",
@@ -4838,7 +4919,8 @@ dependencies = [
[[package]]
name = "libcrux-traits"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e4fa89f3f5e34b47f928b22b1b78395a0d4ec23b1f583db635f128159d65f"
dependencies = [
"libcrux-secrets",
"rand 0.9.2",
@@ -5047,6 +5129,12 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "managed"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]]
name = "maplit"
version = "1.0.2"
@@ -5187,7 +5275,7 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.4.3"
version = "1.4.4"
dependencies = [
"async-trait",
"futures",
@@ -5448,7 +5536,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@@ -5547,7 +5635,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.75"
version = "1.1.77"
dependencies = [
"anyhow",
"async-trait",
@@ -5792,7 +5880,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.72"
version = "1.1.74"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -5875,7 +5963,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.72"
version = "1.1.74"
dependencies = [
"bs58",
"clap",
@@ -6687,6 +6775,7 @@ version = "1.20.4"
dependencies = [
"anyhow",
"base64 0.22.1",
"bs58",
"bytes",
"clap",
"futures",
@@ -6711,7 +6800,6 @@ dependencies = [
"nym-lp",
"nym-network-defaults",
"nym-node-requests",
"nym-node-status-client",
"nym-registration-client",
"nym-registration-common",
"nym-sdk",
@@ -6849,6 +6937,7 @@ dependencies = [
"nym-network-defaults",
"once_cell",
"reqwest 0.13.1",
"rustls 0.23.37",
"serde",
"serde_json",
"serde_plain",
@@ -6956,12 +7045,15 @@ dependencies = [
"nym-crypto",
"nym-service-provider-requests-common",
"nym-sphinx",
"pnet_packet",
"rand 0.8.5",
"semver 1.0.27",
"serde",
"thiserror 2.0.12",
"time",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
@@ -6984,7 +7076,7 @@ dependencies = [
"nym-exit-policy",
"nym-id",
"nym-ip-packet-requests",
"nym-kcp",
"nym-lp",
"nym-network-defaults",
"nym-network-requester",
"nym-sdk",
@@ -7086,6 +7178,7 @@ dependencies = [
"criterion",
"libcrux-psq",
"num_enum",
"nym-common",
"nym-crypto",
"nym-kkt",
"nym-kkt-ciphersuite",
@@ -7279,7 +7372,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.73"
version = "1.1.75"
dependencies = [
"addr",
"anyhow",
@@ -7329,7 +7422,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.27.0"
version = "1.29.0"
dependencies = [
"anyhow",
"arc-swap",
@@ -7466,14 +7559,16 @@ dependencies = [
[[package]]
name = "nym-node-status-agent"
version = "1.1.3"
version = "2.0.0"
dependencies = [
"anyhow",
"clap",
"futures",
"nym-bin-common",
"nym-crypto",
"nym-gateway-probe",
"nym-node-status-client",
"nym-sdk",
"rand 0.8.5",
"regex",
"serde_json",
@@ -7485,7 +7580,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "4.3.0"
version = "4.6.1"
dependencies = [
"ammonia",
"anyhow",
@@ -7550,9 +7645,9 @@ version = "0.3.0"
dependencies = [
"anyhow",
"bincode",
"bs58",
"nym-credentials",
"nym-crypto",
"nym-gateway-probe",
"reqwest 0.13.1",
"serde",
"serde_json",
@@ -7790,6 +7885,8 @@ dependencies = [
"nym-crypto",
"nym-gateway-requests",
"nym-http-api-client",
"nym-ip-packet-requests",
"nym-lp",
"nym-network-defaults",
"nym-ordered-buffer",
"nym-service-providers-common",
@@ -7802,8 +7899,10 @@ dependencies = [
"nym-topology",
"nym-validator-client",
"parking_lot",
"pnet_packet",
"rand 0.8.5",
"reqwest 0.13.1",
"semver 1.0.27",
"serde",
"tap",
"tempfile",
@@ -7875,7 +7974,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.72"
version = "1.1.74"
dependencies = [
"bs58",
"clap",
@@ -8668,11 +8767,12 @@ dependencies = [
"serde",
"thiserror 2.0.12",
"x25519-dalek",
"zeroize",
]
[[package]]
name = "nymvisor"
version = "0.1.37"
version = "0.1.39"
dependencies = [
"anyhow",
"bytes",
@@ -9651,7 +9751,7 @@ dependencies = [
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls 0.23.29",
"rustls 0.23.37",
"socket2 0.5.10",
"thiserror 2.0.12",
"tokio",
@@ -9672,7 +9772,7 @@ dependencies = [
"rand 0.9.2",
"ring",
"rustc-hash",
"rustls 0.23.29",
"rustls 0.23.37",
"rustls-pki-types",
"slab",
"thiserror 2.0.12",
@@ -9939,7 +10039,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.29",
"rustls 0.23.37",
"rustls-native-certs 0.8.3",
"rustls-pki-types",
"serde",
@@ -9980,7 +10080,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.29",
"rustls 0.23.37",
"rustls-pki-types",
"rustls-platform-verifier",
"serde",
@@ -10240,16 +10340,16 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.29"
version = "0.23.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.103.4",
"rustls-webpki 0.103.9",
"subtle 2.6.1",
"zeroize",
]
@@ -10330,14 +10430,14 @@ dependencies = [
"jni",
"log",
"once_cell",
"rustls 0.23.29",
"rustls 0.23.37",
"rustls-native-certs 0.8.3",
"rustls-platform-verifier-android",
"rustls-webpki 0.103.4",
"rustls-webpki 0.103.9",
"security-framework 3.6.0",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@@ -10369,9 +10469,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.103.4"
version = "0.103.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [
"aws-lc-rs",
"ring",
@@ -11062,6 +11162,47 @@ dependencies = [
"serde_core",
]
[[package]]
name = "smolmix"
version = "0.0.1"
dependencies = [
"futures",
"hickory-proto",
"hickory-resolver",
"http-body-util",
"hyper 1.6.0",
"hyper-util",
"nym-bin-common",
"nym-ip-packet-requests",
"nym-sdk",
"reqwest 0.13.1",
"rustls 0.23.37",
"smoltcp",
"thiserror 2.0.12",
"tokio",
"tokio-rustls 0.26.2",
"tokio-smoltcp",
"tokio-tungstenite",
"tracing",
"webpki-roots 0.26.11",
]
[[package]]
name = "smoltcp"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb"
dependencies = [
"bitflags 1.3.2",
"byteorder",
"cfg-if",
"defmt 0.3.100",
"heapless",
"libc",
"log",
"managed",
]
[[package]]
name = "snafu"
version = "0.7.5"
@@ -11202,7 +11343,7 @@ dependencies = [
"memchr",
"once_cell",
"percent-encoding",
"rustls 0.23.29",
"rustls 0.23.37",
"serde",
"serde_json",
"sha2 0.10.9",
@@ -12024,10 +12165,24 @@ version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls 0.23.29",
"rustls 0.23.37",
"tokio",
]
[[package]]
name = "tokio-smoltcp"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5f5d53da1c3095663a8900d86c2abb0ffe02d3f6aa86527b066148fcb33e65e"
dependencies = [
"futures",
"parking_lot",
"pin-project-lite",
"smoltcp",
"tokio",
"tokio-util",
]
[[package]]
name = "tokio-stream"
version = "0.1.17"
@@ -12842,7 +12997,7 @@ dependencies = [
"serde",
"tempfile",
"textwrap",
"toml 0.8.23",
"toml 0.9.12+spec-1.1.0",
"uniffi_internal_macros 0.31.0",
"uniffi_meta 0.31.0",
"uniffi_pipeline 0.31.0",
@@ -12951,7 +13106,7 @@ dependencies = [
"quote",
"serde",
"syn 2.0.106",
"toml 0.8.23",
"toml 0.9.12+spec-1.1.0",
"uniffi_meta 0.31.0",
]
+16 -9
View File
@@ -147,6 +147,7 @@ members = [
"sdk/ffi/go",
"sdk/ffi/shared",
"sdk/rust/nym-sdk",
"smolmix/core",
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
@@ -184,7 +185,6 @@ default-members = [
"nym-api",
"nym-credential-proxy/nym-credential-proxy",
"nym-node",
"nym-node-status-api/nym-node-status-agent",
"nym-statistics-api",
"nym-validator-rewarder",
"nyx-chain-watcher",
@@ -280,6 +280,7 @@ getrandom03 = { package = "getrandom", version = "=0.3.3" }
glob = "0.3"
handlebars = "3.5.5"
hex = "0.4.3"
hickory-proto = "0.25.2"
hickory-resolver = "0.25.2"
hkdf = "0.12.3"
hmac = "0.12.1"
@@ -334,6 +335,7 @@ rayon = "1.5.1"
regex = "1.10.6"
reqwest = { version = "0.13.1", default-features = false }
rs_merkle = "1.5.0"
rustls = { version = "0.23.37", default-features = false }
schemars = "0.8.22"
semver = "1.0.26"
serde = "1.0.219"
@@ -347,6 +349,8 @@ serde_yaml = "0.9.25"
serde_plain = "1.0.2"
sha2 = "0.10.3"
si-scale = "0.2.3"
smolmix = { version = "0.0.1", path = "smolmix/core" }
smoltcp = "0.12"
snow = "0.9.6"
sphinx-packet = "=0.6.0"
sqlx = "0.8.6"
@@ -367,6 +371,8 @@ tokio-postgres = "0.7"
tokio-stream = "0.1.17"
tokio-test = "0.4.4"
tokio-tun = "0.11.5"
tokio-rustls = "0.26"
tokio-smoltcp = "0.5"
tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.15"
toml = "0.8.22"
@@ -398,14 +404,14 @@ prometheus = { version = "0.14.0" }
# libcrux
libcrux-kem = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-ecdh = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-curve25519 = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-chacha20poly1305 = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-psq = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-ml-kem = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-sha3 = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-traits = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
libcrux-kem = "0.0.7"
libcrux-ecdh = "0.0.6"
libcrux-curve25519 = "0.0.6"
libcrux-chacha20poly1305 = "0.0.7"
libcrux-psq = "0.0.8"
libcrux-ml-kem = "0.0.8"
libcrux-sha3 = "0.0.8"
libcrux-traits = "0.0.8"
# Workspace dep definitions required by crates.io publication - we need a workspace version since `cargo workspaces` doesn't work with path imports from crate manifests
nym-api-requests = { version = "1.20.4", path = "nym-api/nym-api-requests" }
@@ -559,6 +565,7 @@ wasm-bindgen = "0.2.99"
wasm-bindgen-futures = "0.4.49"
wasm-bindgen-test = "0.3.49"
wasmtimer = "0.4.1"
webpki-roots = "0.26"
web-sys = "0.3.76"
# for local development:
@@ -60,7 +60,8 @@ packages:
## SYSTEM MAINTENANCE PLAYBOOK KNOBS
###############################################################################
# nym_version: "v2025.21-mozzarella"
# To use particular version instead of Latest, provide in such form:
# nym_version: "nym-binaries-v2026.7-tola"
## NOTE:
## if you want to pin Nym to a specific version instead of using the
@@ -117,4 +118,4 @@ packages:
# enable_writeback_tuning: true
# writeback_dirty_writeback_centisecs: 1500
# writeback_dirty_expire_centisecs: 6000
# writeback_dirty_expire_centisecs: 6000
+25 -6
View File
@@ -1,11 +1,30 @@
---
- name: Configure tunnel manager
- name: Ensure nym binaries directory exists
file:
path: /root/nym-binaries
state: directory
mode: "0755"
tags:
- tunnel
- network_tunnel_manager
become: true
command:
cmd: "/root/nym-binaries/network-tunnel-manager.sh {{ item }}"
- ntm
- name: Download network tunnel manager
get_url:
url: "{{ tunnel_manager_url }}"
dest: /root/nym-binaries/network-tunnel-manager.sh
mode: "0755"
force: yes
tags:
- tunnel
- network_tunnel_manager
- ntm
- name: Run network tunnel manager
command: "/root/nym-binaries/network-tunnel-manager.sh {{ item }}"
loop:
- complete_networking_configuration
register: tunnel_mgr
failed_when: false
tags:
- tunnel
- network_tunnel_manager
- ntm
+3 -3
View File
@@ -1,11 +1,11 @@
[package]
name = "nym-client"
version = "1.1.72"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
version = "1.1.74"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.85"
license.workspace = true
rust-version = "1.85"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -1
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-client-websocket-requests"
description = "Request and response definitions for Nym client websocket connections"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
description = "Request and response definitions for Nym client websocket connections"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+3 -3
View File
@@ -1,11 +1,11 @@
[package]
name = "nym-socks5-client"
version = "1.1.72"
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"
version = "1.1.74"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.85"
license.workspace = true
rust-version = "1.85"
publish = false
[dependencies]
+5 -1
View File
@@ -1,12 +1,16 @@
[package]
name = "nym-async-file-watcher"
description = "Simple file watcher that sends a notification whenever there was any change in the watched file"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
description = "Simple file watcher that sends a notification whenever there was any change in the watched file"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+6 -3
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-authenticator-requests"
description = "Crate defining requests and responses for the Nym authenticator client"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Crate defining requests and responses for the Nym authenticator client"
rust-version.workspace = true
readme.workspace = true
publish = true
[dependencies]
base64 = { workspace = true }
+5 -1
View File
@@ -1,12 +1,16 @@
[package]
name = "nym-bandwidth-controller"
description = "Crate for controlling the use of zknym credentials to ensure constant bandwidth availability for NymVPN app"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Crate for controlling the use of zknym credentials to ensure constant bandwidth availability for NymVPN app"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -34,7 +34,7 @@ where
let signing_key = ed25519::PrivateKey::new(&mut rng);
let expiration = expiration.unwrap_or_else(ecash_default_expiration_date);
let deposit_amount = client.get_required_deposit_amount().await?;
let deposit_amount = client.get_default_deposit_amount().await?;
info!("we'll need to deposit {deposit_amount} to obtain the ticketbook");
let result = client
.make_ticketbook_deposit(
+8 -2
View File
@@ -1,11 +1,16 @@
[package]
name = "nym-bin-common"
version.workspace = true
description = "Common code for nym binaries"
edition = { workspace = true }
version.workspace = true
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
[dependencies]
clap = { workspace = true, features = ["derive"], optional = true }
@@ -38,6 +43,7 @@ default = []
openapi = ["utoipa"]
output_format = ["serde_json", "dep:clap"]
bin_info_schema = ["schemars"]
ip_check = []
basic_tracing = ["dep:tracing", "dep:tracing-subscriber"]
otel-otlp = [
"basic_tracing",
@@ -1,29 +1,12 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
// use `ip` feature without nightly
// issue: https://github.com/rust-lang/rust/issues/27709
pub(crate) const fn is_global_ip(ip: &IpAddr) -> bool {
pub const fn is_global_ip(ip: &IpAddr) -> bool {
match ip {
IpAddr::V4(addr) => is_global_ipv4(addr),
IpAddr::V6(addr) => is_global_ipv6(addr),
}
}
const fn is_shared_ipv4(ip: &Ipv4Addr) -> bool {
ip.octets()[0] == 100 && (ip.octets()[1] & 0b1100_0000 == 0b0100_0000)
}
const fn is_benchmarking_ipv4(ip: &Ipv4Addr) -> bool {
ip.octets()[0] == 198 && (ip.octets()[1] & 0xfe) == 18
}
const fn is_reserved_ipv4(ip: &Ipv4Addr) -> bool {
ip.octets()[0] & 240 == 240 && !ip.is_broadcast()
}
const fn is_global_ipv4(ip: &Ipv4Addr) -> bool {
!(ip.octets()[0] == 0 // "This network"
|| ip.is_private()
@@ -42,6 +25,18 @@ const fn is_global_ipv4(ip: &Ipv4Addr) -> bool {
|| ip.is_broadcast())
}
const fn is_shared_ipv4(ip: &Ipv4Addr) -> bool {
ip.octets()[0] == 100 && (ip.octets()[1] & 0b1100_0000 == 0b0100_0000)
}
const fn is_benchmarking_ipv4(ip: &Ipv4Addr) -> bool {
ip.octets()[0] == 198 && (ip.octets()[1] & 0xfe) == 18
}
const fn is_reserved_ipv4(ip: &Ipv4Addr) -> bool {
ip.octets()[0] & 240 == 240 && !ip.is_broadcast()
}
const fn is_documentation_ipv6(ip: &Ipv6Addr) -> bool {
(ip.segments()[0] == 0x2001) && (ip.segments()[1] == 0xdb8)
}
+3
View File
@@ -9,3 +9,6 @@ pub mod completions;
#[cfg(feature = "output_format")]
pub mod output_format;
#[cfg(feature = "ip_check")]
pub mod ip_check;
+4 -2
View File
@@ -1,14 +1,16 @@
[package]
name = "nym-client-core"
description = "Crate containing core client functionality and configs, used by all other Nym client implentations"
version.workspace = true
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2024"
rust-version = "1.85"
license.workspace = true
description = "Crate containing core client functionality and configs, used by all other Nym client implentations"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+5 -1
View File
@@ -1,12 +1,16 @@
[package]
name = "nym-client-core-config-types"
description = "Low level configs and constants used by Nym clients and nodes"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Low level configs and constants used by Nym clients and nodes"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -1,13 +1,16 @@
[package]
name = "nym-client-core-gateways-storage"
description = "Functionality for Nym clients to store and retrive Gateway connections"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
rust-version.workspace = true
description = "Functionality for Nym clients to store and retrive Gateway connections"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+5 -1
View File
@@ -1,12 +1,16 @@
[package]
name = "nym-client-core-surb-storage"
description = "Functionality for Nym clients to generate and use Single Use Reply Blocks (SURBs)"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Functionality for Nym clients to generate and use Single Use Reply Blocks (SURBs)"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -1
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-gateway-client"
description = "Functions and types for Nym client <> Gateway connections"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
description = "Functions and types for Nym client <> Gateway connections"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -1
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-mixnet-client"
description = "Client for Mix Node <> Mix Node & Mix Node <> Gateway communication"
version.workspace = true
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
description = "Client for Mix Node <> Mix Node & Mix Node <> Gateway communication"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -342,7 +342,7 @@ impl SendWithoutResponse for Client {
sending_res.map_err(|err| {
match err {
TrySendError::Full(_) => {
warn!(
trace!(
event = "mixclient.try_send",
peer = %address,
result = "full_dropped",
@@ -1,14 +1,16 @@
[package]
name = "nym-validator-client"
description = "Client for interacting with Nyx Cosmos SDK blockchain"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.85"
license.workspace = true
description = "Client for interacting with Nyx Cosmos SDK blockchain"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -8,6 +8,7 @@ use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cosmwasm_std::Coin;
use nym_ecash_contract_common::deposit::LatestDepositResponse;
use nym_ecash_contract_common::deposit_statistics::DepositsStatistics;
use nym_ecash_contract_common::msg::QueryMsg as EcashQueryMsg;
use serde::Deserialize;
@@ -17,6 +18,9 @@ pub use nym_ecash_contract_common::blacklist::{
pub use nym_ecash_contract_common::deposit::{
Deposit, DepositData, DepositId, DepositResponse, PagedDepositsResponse,
};
pub use nym_ecash_contract_common::reduced_deposit::{
WhitelistedAccount, WhitelistedAccountsResponse,
};
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@@ -42,8 +46,18 @@ pub trait EcashQueryClient {
.await
}
async fn get_required_deposit_amount(&self) -> Result<Coin, NyxdError> {
self.query_ecash_contract(EcashQueryMsg::GetRequiredDepositAmount {})
async fn get_default_deposit_amount(&self) -> Result<Coin, NyxdError> {
self.query_ecash_contract(EcashQueryMsg::GetDefaultDepositAmount {})
.await
}
async fn get_reduced_deposit_amount(&self, address: String) -> Result<Option<Coin>, NyxdError> {
self.query_ecash_contract(EcashQueryMsg::GetReducedDepositAmount { address })
.await
}
async fn get_all_whitelisted_accounts(&self) -> Result<WhitelistedAccountsResponse, NyxdError> {
self.query_ecash_contract(EcashQueryMsg::GetAllWhitelistedAccounts {})
.await
}
@@ -65,6 +79,11 @@ pub trait EcashQueryClient {
self.query_ecash_contract(EcashQueryMsg::GetDepositsPaged { start_after, limit })
.await
}
async fn get_deposits_statistics(&self) -> Result<DepositsStatistics, NyxdError> {
self.query_ecash_contract(EcashQueryMsg::GetDepositsStatistics {})
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -122,10 +141,17 @@ mod tests {
EcashQueryMsg::GetDepositsPaged { limit, start_after } => {
client.get_deposits_paged(start_after, limit).ignore()
}
EcashQueryMsg::GetRequiredDepositAmount {} => {
client.get_required_deposit_amount().ignore()
EcashQueryMsg::GetDefaultDepositAmount {} => {
client.get_default_deposit_amount().ignore()
}
EcashQueryMsg::GetReducedDepositAmount { address } => {
client.get_reduced_deposit_amount(address).ignore()
}
EcashQueryMsg::GetAllWhitelistedAccounts {} => {
client.get_all_whitelisted_accounts().ignore()
}
EcashQueryMsg::GetLatestDeposit {} => client.get_latest_deposit().ignore(),
EcashQueryMsg::GetDepositsStatistics {} => client.get_deposits_statistics().ignore(),
};
}
}
@@ -62,13 +62,47 @@ pub trait EcashSigningClient {
new_deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = EcashExecuteMsg::UpdateDepositValue {
let req = EcashExecuteMsg::UpdateDefaultDepositValue {
new_deposit: new_deposit.into(),
};
self.execute_ecash_contract(fee, req, "Ecash::UpdateDepositValue".to_string(), vec![])
.await
}
async fn set_reduced_deposit_price(
&self,
address: String,
deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = EcashExecuteMsg::SetReducedDepositPrice {
address,
deposit: deposit.into(),
};
self.execute_ecash_contract(
fee,
req,
"Ecash::SetReducedDepositPrice".to_string(),
vec![],
)
.await
}
async fn remove_reduced_deposit_price(
&self,
address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = EcashExecuteMsg::RemoveReducedDepositPrice { address };
self.execute_ecash_contract(
fee,
req,
"Ecash::RemoveReducedDepositPrice".to_string(),
vec![],
)
.await
}
async fn propose_for_blacklist(
&self,
public_key: String,
@@ -141,9 +175,15 @@ mod tests {
.ignore(),
ExecuteMsg::RedeemTickets { .. } => unimplemented!(), // no redeem tickets method for the client
ExecuteMsg::UpdateAdmin { admin } => client.update_admin(admin, None).ignore(),
ExecuteMsg::UpdateDepositValue { new_deposit } => client
ExecuteMsg::UpdateDefaultDepositValue { new_deposit } => client
.update_deposit_value(new_deposit.into(), None)
.ignore(),
ExecuteMsg::SetReducedDepositPrice { address, deposit } => client
.set_reduced_deposit_price(address, deposit.into(), None)
.ignore(),
ExecuteMsg::RemoveReducedDepositPrice { address } => {
client.remove_reduced_deposit_price(address, None).ignore()
}
};
}
}
+4 -1
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-cli-commands"
description = "Common commands crate used by the nym-cli tool for interacting with the Nyx Cosmos SDK blockchain and Mixnet endpoints"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Common commands crate used by the nym-cli tool for interacting with the Nyx Cosmos SDK blockchain and Mixnet endpoints"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
[dependencies]
anyhow = { workspace = true }
+6 -1
View File
@@ -1,11 +1,16 @@
[package]
name = "nym-config"
description = "Config related helpers and functions"
version.workspace = true
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
repository.workspace = true
homepage.workspace = true
description = "Config related helpers and functions"
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -1,12 +1,16 @@
[package]
name = "nym-coconut-dkg-common"
description = "Common crate for Nym's DKG cosmwasm contract"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Common crate for Nym's DKG cosmwasm contract"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -1,15 +1,16 @@
[package]
name = "nym-contracts-common-testing"
description = "Common crate for cosmwasm contract tests"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
rust-version = "1.85"
readme.workspace = true
description = "Common crate for cosmwasm contract tests"
publish = true
[dependencies]
anyhow = { workspace = true }
@@ -1,11 +1,16 @@
[package]
name = "nym-contracts-common"
version.workspace = true
description = "Common library for Nym cosmwasm contracts"
edition = { workspace = true }
version.workspace = true
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
[dependencies]
bs58 = { workspace = true }
@@ -2,8 +2,8 @@
name = "easy-addr"
version.workspace = true
edition = "2021"
publish = false
license.workspace = true
publish = false
[lib]
proc-macro = true
@@ -1,12 +1,16 @@
[package]
name = "nym-ecash-contract-common"
description = "Common crate for Nym's ecash/zknym cosmwasm contract"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Common crate for Nym's ecash/zknym cosmwasm contract"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -6,6 +6,14 @@ use cosmwasm_std::Coin;
#[cw_serde]
pub struct PoolCounters {
/// Represents the total amount of funds deposited into the contract.
pub total_deposited: Coin,
/// Represents the total amount of funds redeemed from the contract that got transferred into the holding account.
pub total_redeemed: Coin,
/// Represents the total amount of tickets requested to be redeemed from the contract and get moved into the holding account,
/// after that functionality got disabled.
#[serde(default)]
pub tickets_requested_and_not_redeemed: u64,
}
@@ -0,0 +1,38 @@
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
use std::collections::HashMap;
/// Aggregate statistics about all deposits made through the ecash contract.
#[cw_serde]
pub struct DepositsStatistics {
/// Total number of deposits ever made (at any price tier),
/// derived from the deposit id counter.
pub total_deposits_made: u32,
/// Total value of all deposits ever made (at any price tier),
/// sourced from `PoolCounters::total_deposited`.
pub total_deposited: Coin,
/// Number of deposits made at the default (non-reduced) price.
pub total_deposits_made_with_default_price: u32,
/// Total value deposited at the default price.
pub total_deposited_with_default_price: Coin,
/// Number of deposits made at any custom (reduced) price, summed across all whitelisted accounts.
pub total_deposits_made_with_custom_price: u32,
/// Total value deposited at custom prices, summed across all whitelisted accounts.
pub total_deposited_with_custom_price: Coin,
/// Per-account breakdown of deposit counts for whitelisted addresses.
// note: we use String for addressing due to serialisation incompatibility
pub deposits_made_with_custom_price: HashMap<String, u32>,
/// Per-account breakdown of deposited amounts for whitelisted addresses.
// note: we use String for addressing due to serialisation incompatibility
pub deposited_with_custom_price: HashMap<String, Coin>,
}
@@ -65,4 +65,26 @@ pub enum EcashContractError {
#[error("the account blacklisting hasn't been fully implemented yet")]
UnimplementedBlacklisting,
#[error("reduced deposit must use the same denom as the default deposit (expected '{expected}', got '{got}')")]
InvalidReducedDepositDenom { expected: String, got: String },
#[error(
"reduced deposit amount ({reduced}) must be strictly less than the default ({default})"
)]
ReducedDepositNotReduced {
reduced: cosmwasm_std::Uint128,
default: cosmwasm_std::Uint128,
},
#[error("address '{address}' does not have a custom reduced deposit price set")]
NoReducedDepositPrice { address: String },
#[error(
"deposit amount ({amount}) must be at least the ticket book size ({ticket_book_size})"
)]
DepositBelowTicketBookSize {
amount: cosmwasm_std::Uint128,
ticket_book_size: u64,
},
}
@@ -4,10 +4,12 @@
pub mod blacklist;
pub mod counters;
pub mod deposit;
pub mod deposit_statistics;
pub mod error;
pub mod event_attributes;
pub mod events;
pub mod msg;
pub mod redeem_credential;
pub mod reduced_deposit;
pub use error::EcashContractError;
@@ -9,6 +9,10 @@ use crate::blacklist::{BlacklistedAccountResponse, PagedBlacklistedAccountRespon
#[cfg(feature = "schema")]
use crate::deposit::{DepositResponse, LatestDepositResponse, PagedDepositsResponse};
#[cfg(feature = "schema")]
use crate::deposit_statistics::DepositsStatistics;
#[cfg(feature = "schema")]
use crate::reduced_deposit::WhitelistedAccountsResponse;
#[cfg(feature = "schema")]
use cosmwasm_schema::QueryResponses;
#[cw_serde]
@@ -42,10 +46,25 @@ pub enum ExecuteMsg {
admin: String,
},
UpdateDepositValue {
#[serde(alias = "update_deposit_value")]
UpdateDefaultDepositValue {
new_deposit: Coin,
},
/// Set (or overwrite) a reduced deposit price for a specific address.
/// Only callable by the contract admin.
SetReducedDepositPrice {
address: String,
deposit: Coin,
},
/// Remove the reduced deposit price for a specific address, reverting them to
/// the default price. Returns an error if the address has no custom price set.
/// Only callable by the contract admin.
RemoveReducedDepositPrice {
address: String,
},
// TODO: properly implement
ProposeToBlacklist {
public_key: String,
@@ -68,7 +87,15 @@ pub enum QueryMsg {
},
#[cfg_attr(feature = "schema", returns(Coin))]
GetRequiredDepositAmount {},
#[serde(alias = "get_required_deposit_amount")]
#[serde(alias = "GetRequiredDepositAmount")]
GetDefaultDepositAmount {},
#[cfg_attr(feature = "schema", returns(Option<Coin>))]
GetReducedDepositAmount { address: String },
#[cfg_attr(feature = "schema", returns(WhitelistedAccountsResponse))]
GetAllWhitelistedAccounts {},
#[cfg_attr(feature = "schema", returns(DepositResponse))]
GetDeposit { deposit_id: u32 },
@@ -81,7 +108,22 @@ pub enum QueryMsg {
limit: Option<u32>,
start_after: Option<u32>,
},
#[cfg_attr(feature = "schema", returns(DepositsStatistics))]
GetDepositsStatistics {},
}
#[cw_serde]
pub struct MigrateMsg {}
pub struct MigrateMsg {
/// Initial set of whitelisted accounts with their reduced deposit prices.
/// Each entry is validated and stored during migration.
pub initial_whitelist: Vec<WhitelistedDeposit>,
}
/// An address and its reduced deposit price, used when seeding the whitelist
/// via migration.
#[cw_serde]
pub struct WhitelistedDeposit {
pub address: String,
pub deposit: Coin,
}
@@ -0,0 +1,16 @@
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Coin};
#[cw_serde]
pub struct WhitelistedAccount {
pub address: Addr,
pub deposit: Coin,
}
#[cw_serde]
pub struct WhitelistedAccountsResponse {
pub whitelisted_accounts: Vec<WhitelistedAccount>,
}
@@ -1,12 +1,16 @@
[package]
name = "nym-group-contract-common"
description = "Common crate for Nym's group cosmwasm contract"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Common crate for Nym's group cosmwasm contract"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
[dependencies]
cosmwasm-schema = { workspace = true }
@@ -1,12 +1,16 @@
[package]
name = "nym-mixnet-contract-common"
version.workspace = true
description = "Common library for the Nym mixnet contract"
rust-version = "1.85"
edition = { workspace = true }
version.workspace = true
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
[dependencies]
bs58 = { workspace = true }
@@ -1,10 +1,16 @@
[package]
name = "nym-multisig-contract-common"
description = "Common code for the Nym multisig CosmWasm smart contract"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Common code for the Nym multisig CosmWasm smart contract"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
[dependencies]
cosmwasm-schema = { workspace = true }
@@ -1,15 +1,16 @@
[package]
name = "nym-performance-contract-common"
description = "Common crate for Nym's group performance contract"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
rust-version = "1.85"
readme.workspace = true
description = "Common crate for Nym's group performance contract"
publish = true
[dependencies]
thiserror = { workspace = true }
@@ -1,15 +1,16 @@
[package]
name = "nym-pool-contract-common"
version.workspace = true
description = "Common library for the Nym Pool contract"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
[dependencies]
thiserror = { workspace = true }
@@ -1,11 +1,16 @@
[package]
name = "nym-vesting-contract-common"
version.workspace = true
description = "Common library for the Nym vesting contract"
edition = { workspace = true }
version.workspace = true
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
homepage.workspace = true
documentation.workspace = true
rust-version = "1.85"
readme.workspace = true
publish = true
[dependencies]
cosmwasm-std = { workspace = true }
+4 -3
View File
@@ -1,15 +1,16 @@
[package]
name = "nym-credential-proxy-lib"
description = "Build script and core functionality of the Nym Credential Proxy"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
description = "Build script and core functionality of the Nym Credential Proxy"
publish = true
[dependencies]
anyhow = { workspace = true }
@@ -6,7 +6,7 @@ use crate::helpers::LockTimer;
use nym_ecash_contract_common::msg::ExecuteMsg;
use nym_validator_client::nyxd::contract_traits::NymContractsProvider;
use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult;
use nym_validator_client::nyxd::{Coin, Config, CosmWasmClient, NyxdClient};
use nym_validator_client::nyxd::{AccountId, Coin, Config, CosmWasmClient, NyxdClient};
use nym_validator_client::{DirectSigningHttpRpcNyxdClient, nyxd};
use std::ops::Deref;
use std::sync::Arc;
@@ -50,6 +50,10 @@ impl ChainClient {
Ok(ChainClient(Arc::new(RwLock::new(client))))
}
pub async fn address(&self) -> AccountId {
self.0.read().await.address()
}
pub async fn query_chain(&self) -> ChainReadPermit<'_> {
let _acquire_timer = LockTimer::new("acquire chain query permit");
self.0.read().await
@@ -8,6 +8,7 @@ use nym_validator_client::nyxd::contract_traits::EcashQueryClient;
use std::sync::Arc;
use time::OffsetDateTime;
use tokio::sync::RwLock;
use tracing::{info, warn};
pub struct CachedDeposit {
valid_until: OffsetDateTime,
@@ -56,13 +57,29 @@ impl RequiredDepositCache {
// update cache
drop(read_guard);
let address = chain_client.address().await;
info!("checking deposit required by {address}");
let mut write_guard = self.inner.write().await;
let deposit_amount = chain_client
.query_chain()
.await
.get_required_deposit_amount()
let read_permit = chain_client.query_chain().await;
let reduced = read_permit
.get_reduced_deposit_amount(address.to_string())
.await?;
let deposit_amount = match reduced {
Some(reduced) => {
info!("we're permitted to use reduced price");
reduced
}
None => {
warn!(
"using default deposit value {address} is not whitelisted for price reduction"
);
read_permit.get_default_deposit_amount().await?
}
};
let nym_coin: Coin = deposit_amount.into();
write_guard.update(nym_coin.clone());
+5 -2
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-credential-storage"
description = "Crate for handling and storing spent and unspent zknym ticketbooks"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
rust-version.workspace = true
description = "Crate for handling and storing spent and unspent zknym ticketbooks"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+5 -1
View File
@@ -1,12 +1,16 @@
[package]
name = "nym-credential-utils"
description = "Utils crate for dealing with zknym credentials"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Utils crate for dealing with zknym credentials"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+4 -3
View File
@@ -1,15 +1,16 @@
[package]
name = "nym-credential-verification"
description = "Store and verify zknym credentials"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
description = "Store and verify zknym credentials"
publish = true
[dependencies]
async-trait = { workspace = true }
@@ -3,25 +3,19 @@
use crate::Error;
use crate::ecash::error::EcashTicketError;
use crate::ecash::helpers::for_each_api_concurrent;
use crate::ecash::state::SharedState;
use cosmwasm_std::Fraction;
use cw_utils::ThresholdResponse;
use futures::channel::mpsc::UnboundedReceiver;
use futures::{Stream, StreamExt};
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
use nym_api_requests::ecash::models::VerifyEcashTicketBody;
use nym_credentials_interface::Bandwidth;
use nym_credentials_interface::{ClientTicket, TicketType};
use nym_validator_client::EcashApiClient;
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
use nym_validator_client::nym_api::NymApiClientExt;
use nym_validator_client::nyxd::AccountId;
use nym_validator_client::nyxd::contract_traits::{
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
};
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData;
use nym_validator_client::nyxd::cw3::Status;
use nym_validator_client::nyxd::contract_traits::MultisigQueryClient;
use si_scale::helpers::bibytes2;
use std::collections::{HashMap, HashSet};
use std::ops::Deref;
@@ -31,22 +25,6 @@ use tokio::sync::{Mutex, RwLockReadGuard};
use tokio::time::{Duration, Instant, interval_at};
use tracing::{debug, error, info, instrument, trace, warn};
enum ProposalResult {
Executed,
Rejected,
Pending,
}
impl ProposalResult {
fn is_pending(&self) -> bool {
matches!(self, ProposalResult::Pending)
}
fn is_rejected(&self) -> bool {
matches!(self, ProposalResult::Rejected)
}
}
struct PendingVerification {
ticket: ClientTicket,
@@ -68,43 +46,6 @@ impl PendingVerification {
}
}
struct PendingRedemptionVote {
proposal_id: u64,
digest: Vec<u8>,
included_serial_numbers: Vec<Vec<u8>>,
epoch_id: EpochId,
// vec of node ids of apis that haven't sent a valid response
pending: Vec<u64>,
}
impl PendingRedemptionVote {
fn new(
proposal_id: u64,
digest: Vec<u8>,
included_serial_numbers: Vec<Vec<u8>>,
epoch_id: EpochId,
pending: Vec<u64>,
) -> Self {
PendingRedemptionVote {
proposal_id,
digest,
included_serial_numbers,
epoch_id,
pending,
}
}
fn to_request_body(&self, gateway_cosmos_addr: AccountId) -> BatchRedeemTicketsBody {
BatchRedeemTicketsBody::new(
self.digest.clone(),
self.proposal_id,
self.included_serial_numbers.clone(),
gateway_cosmos_addr,
)
}
}
pub struct CredentialHandlerConfig {
/// Specifies the multiplier for revoking a malformed/double-spent ticket
/// (if it has to go all the way to the nym-api for verification)
@@ -132,7 +73,6 @@ pub struct CredentialHandler {
ticket_receiver: UnboundedReceiver<ClientTicket>,
shared_state: SharedState,
pending_tickets: Vec<PendingVerification>,
pending_redemptions: Vec<PendingRedemptionVote>,
}
impl CredentialHandler {
@@ -184,75 +124,6 @@ impl CredentialHandler {
Ok(pending)
}
async fn rebuild_pending_votes(
shared_state: &SharedState,
) -> Result<Vec<PendingRedemptionVote>, EcashTicketError> {
// 1. get all tickets that were not fully verified
let unverified = shared_state.storage.get_all_unresolved_proposals().await?;
let mut pending = Vec::with_capacity(unverified.len());
let epoch_id = shared_state.current_epoch_id().await?;
let apis = shared_state
.api_clients(epoch_id)
.await?
.iter()
.map(|s| (s.cosmos_address.to_string(), s.node_id))
.collect::<Vec<_>>();
for proposal_id in unverified {
// get all of the votes
let votes = shared_state
.start_query()
.await
.get_all_votes(proposal_id as u64)
.await
.map_err(EcashTicketError::chain_query_failure)?
.into_iter()
.map(|v| v.voter)
.collect::<HashSet<_>>();
let mut missing_votes = Vec::new();
// see who hasn't voted
for (api_address, api_id) in &apis {
// for each signer, check if they have actually voted; if not, that's the missing guy
if !votes.contains(api_address) {
missing_votes.push(*api_id)
}
}
// attempt to rebuild SN and digest from the proposal info + storage data
let proposal_info = shared_state
.start_query()
.await
.query_proposal(proposal_id as u64)
.await
.map_err(EcashTicketError::chain_query_failure)?;
let tickets = shared_state
.storage
.get_all_proposed_tickets_with_sn(proposal_id as u32)
.await?;
let digest =
BatchRedeemTicketsBody::make_digest(tickets.iter().map(|t| &t.serial_number));
let encoded_digest = bs58::encode(&digest).into_string();
if encoded_digest != proposal_info.description {
error!("the lost proposal {proposal_id} does not have a matching digest!");
continue;
}
pending.push(PendingRedemptionVote {
proposal_id: proposal_id as u64,
digest,
included_serial_numbers: tickets.into_iter().map(|t| t.serial_number).collect(),
epoch_id,
pending: missing_votes,
})
}
Ok(pending)
}
pub(crate) async fn new(
config: CredentialHandlerConfig,
ticket_receiver: UnboundedReceiver<ClientTicket>,
@@ -276,51 +147,15 @@ impl CredentialHandler {
// on startup read pending credentials and api responses from the storage
let pending_tickets = Self::rebuild_pending_tickets(&shared_state).await?;
// on startup read pending proposals from the storage
// then reconstruct the votes by querying the multisig contract for votes on those proposals
// digest from the description and count from the message
let pending_redemptions = Self::rebuild_pending_votes(&shared_state).await?;
Ok(CredentialHandler {
config,
multisig_threshold,
ticket_receiver,
shared_state,
pending_tickets,
pending_redemptions,
})
}
// the argument is temporary as we'll be reading from the storage
async fn create_redemption_proposal(
&self,
commitment: &[u8],
number_of_tickets: u16,
) -> Result<u64, EcashTicketError> {
let res = self
.shared_state
.start_tx()
.await
.request_ticket_redemption(
bs58::encode(commitment).into_string(),
number_of_tickets,
None,
)
.await
.map_err(|source| EcashTicketError::RedemptionProposalCreationFailure { source })?;
// that one is quite tricky because proposal exists on chain, but we didn't get the id...
// but it should be quite impossible to ever reach this unless we make breaking changes
let proposal_id = res
.parse_singleton_u64_contract_data()
.inspect_err(|err| error!("reached seemingly impossible error! could not recover the redemption proposal id: {err}"))
.map_err(|source| EcashTicketError::ProposalIdParsingFailure { source })?;
info!("created redemption proposal {proposal_id} to redeem {number_of_tickets} tickets");
Ok(proposal_id)
}
/// Attempt to send ticket verification request to the provided ecash verifier.
async fn verify_ticket(
&self,
@@ -522,42 +357,7 @@ impl CredentialHandler {
async fn resolve_pending(&mut self) -> Result<(), EcashTicketError> {
let mut still_failing = Vec::new();
// 1. attempt to resolve all pending proposals
while let Some(mut pending) = self.pending_redemptions.pop() {
match self.try_resolve_pending_proposal(&mut pending, None).await {
Ok(resolution) => {
if resolution.is_pending() {
warn!(
"still failed to reach quorum for proposal {}. apis: {:?} haven't responded. we'll retry later",
pending.proposal_id, pending.pending
);
still_failing.push(pending);
} else {
self.shared_state
.storage
.clear_post_proposal_data(
pending.proposal_id as u32,
OffsetDateTime::now_utc(),
resolution.is_rejected(),
)
.await?;
}
}
Err(err) => {
error!(
"experienced internal error when attempting to resolve pending proposal: {err}"
);
// make sure to update internal state to not lose any data
self.pending_redemptions.push(pending);
self.pending_redemptions.append(&mut still_failing);
return Err(err);
}
}
}
let mut still_failing = Vec::new();
// 2. attempt to verify the remaining tickets
// 1. attempt to verify the remaining tickets
while let Some(mut pending) = self.pending_tickets.pop() {
// possible optimisation: if there's a lot of pending tickets, pre-emptively grab locks for api_clients
match self
@@ -595,362 +395,14 @@ impl CredentialHandler {
Ok(())
}
/// Attempt to send batch redemption request to the provided ecash verifier.
async fn redeem_tickets(
&self,
proposal_id: u64,
request: &BatchRedeemTicketsBody,
client: &EcashApiClient,
) -> Result<bool, EcashTicketError> {
match client.api_client.batch_redeem_ecash_tickets(request).await {
Ok(res) => {
let accepted = if res.proposal_accepted {
trace!("{client} has accepted proposal {proposal_id}");
true
} else {
warn!("{client} has rejected proposal {proposal_id}");
false
};
Ok(accepted)
}
Err(err) => {
error!(
"failed to send proposal {proposal_id} for redemption vote to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later"
);
Ok(false)
}
}
}
async fn try_execute_proposal(&self, proposal_id: u64) -> Result<(), EcashTicketError> {
self.shared_state
.start_tx()
.await
.execute_proposal(proposal_id, None)
.await
.map_err(
|source| EcashTicketError::RedemptionProposalExecutionFailure {
proposal_id,
source,
},
)?;
Ok(())
}
async fn get_proposal_status(&self, proposal_id: u64) -> Result<Status, EcashTicketError> {
Ok(self
.shared_state
.start_query()
.await
.query_proposal(proposal_id)
.await
.map_err(EcashTicketError::chain_query_failure)?
.status)
}
async fn try_finalize_proposal(
&self,
proposal_id: u64,
) -> Result<ProposalResult, EcashTicketError> {
match self.get_proposal_status(proposal_id).await? {
Status::Pending => {
// the voting hasn't even begun!
error!("impossible case! the proposal {proposal_id} is still pending");
Ok(ProposalResult::Pending)
}
Status::Open => {
debug!("proposal {proposal_id} is still open and needs more votes");
Ok(ProposalResult::Pending)
}
Status::Rejected => {
warn!("proposal {proposal_id} has been rejected");
Ok(ProposalResult::Rejected)
}
Status::Passed => {
info!(
"proposal {proposal_id} has already been passed - we just need to execute it"
);
self.try_execute_proposal(proposal_id).await?;
info!("executed proposal {proposal_id}");
Ok(ProposalResult::Executed)
}
Status::Executed => {
info!("proposal {proposal_id} has already been executed - nothing to do!");
Ok(ProposalResult::Executed)
}
}
}
async fn try_resolve_pending_proposal(
&self,
pending: &mut PendingRedemptionVote,
api_clients: Option<RwLockReadGuard<'_, Vec<EcashApiClient>>>,
) -> Result<ProposalResult, EcashTicketError> {
let proposal_id = pending.proposal_id;
info!(
"attempting to resolve pending redemption proposal {proposal_id} to redeem {} tickets",
pending.included_serial_numbers.len()
);
// check if the proposal still needs more votes from the apis
let result = self.try_finalize_proposal(proposal_id).await?;
if !result.is_pending() {
return Ok(result);
}
let api_clients = match api_clients {
Some(clients) => clients,
None => self.shared_state.api_clients(pending.epoch_id).await?,
};
let redemption_request = pending.to_request_body(self.shared_state.address.clone());
// TODO: optimisation: tell other apis they can purge our tickets even if they haven't voted
let total = api_clients.len();
let api_failures = Mutex::new(Vec::new());
let rejected = AtomicUsize::new(0);
for_each_api_concurrent(&api_clients, &pending.pending, |ecash_client| async {
// errors are only returned on hard, storage, failures
match self
.redeem_tickets(pending.proposal_id, &redemption_request, ecash_client)
.await
{
Err(err) => {
error!("internal failure. could not proceed with ticket redemption: {err}");
api_failures.lock().await.push(ecash_client.node_id);
}
Ok(false) => {
rejected.fetch_add(1, Ordering::SeqCst);
}
_ => {}
}
})
.await;
let api_failures = api_failures.into_inner();
let num_failures = api_failures.len();
pending.pending = api_failures;
let rejected = rejected.into_inner();
let rejected_ratio = rejected as f32 / total as f32;
let rejected_perc = rejected_ratio * 100.;
if rejected_ratio >= (1. - self.multisig_threshold) {
error!(
"{rejected_perc:.2}% of signers rejected proposal {proposal_id}. we won't be able to execute it"
);
// no need to query the chain as with so many rejections it's impossible it has passed.
return Ok(ProposalResult::Rejected);
}
let accepted_ratio = (total - rejected - num_failures) as f32 / total as f32;
let accepted_perc = accepted_ratio * 100.;
match accepted_ratio {
n if n < self.multisig_threshold => {
error!(
"less than 2/3 of signers ({accepted_perc:.2}%) accepted proposal {proposal_id}. we're not yet be able to execute it to get funds out"
);
return Ok(ProposalResult::Pending);
}
n if n < self.config.minimum_api_quorum => {
warn!(
"the system seems to be a bit unstable: less than 80%, but more than 67% of signers ({accepted_perc:.2}%) accepted proposal {proposal_id}"
);
}
_ => {
trace!("{accepted_perc:.2}% of signers accepted proposal {proposal_id}");
}
}
// attempt to execute the proposal if it reached the required threshold
self.try_finalize_proposal(proposal_id).await
}
async fn maybe_redeem_tickets(&mut self) -> Result<(), EcashTicketError> {
if !self.pending_tickets.is_empty() {
return Err(EcashTicketError::PendingTickets);
}
let latest_stored = self.shared_state.storage.latest_proposal().await?;
// check if we have already created the proposal but crashed before persisting it in the db
//
// if we have some persisted proposals in storage, try to see if there's anything more recent on chain
// (i.e. the missing proposal)
// if not (i.e. this would have been our first) check the latest page of proposals.
// while this is not ideal, realistically speaking we probably crashed few minutes ago
// and worst case scenario we'll just recreate the proposal instead
//
// LIMITATION: if MULTIPLE proposals got created in between, well. though luck.
let latest_on_chain = if let Some(latest_stored) = &latest_stored {
// those are sorted in ASCENDING way
self.shared_state
.proposals_since(latest_stored.proposal_id as u64)
.await?
.pop()
} else {
// but those are DESCENDING
self.shared_state
.last_proposal_page()
.await?
.first()
.cloned()
};
let now = OffsetDateTime::now_utc();
let prior_proposal = match (&latest_stored, latest_on_chain) {
(None, None) => {
// we haven't created any proposals before
trace!("this could be our first redemption proposal");
None
}
(Some(stored), None) => {
if stored.created_at + MIN_BATCH_REDEMPTION_DELAY > now {
trace!("too soon to create new redemption proposal");
return Ok(());
}
None
}
(_, Some(on_chain)) => {
warn!(
"we seem to have crashed after creating proposal, but before persisting it onto disk!"
);
Some(on_chain)
}
};
// technically we could have been just caching all of those serial numbers as we verify tickets,
// but given how infrequently we call this, there's no point in wasting this memory
let verified_tickets = self
.shared_state
.storage
.get_all_verified_tickets_with_sn()
.await?;
// TODO: somehow simplify that nasty nested if
if verified_tickets.len() < self.config.minimum_redemption_tickets {
// bypass the number of tickets check if we're about to lose our rewards due to expiration
if let Some(latest_stored) = latest_stored {
if latest_stored.created_at + self.config.maximum_time_between_redemption < now {
{}
} else {
debug!(
"we only have {} verified tickets. there's no point in creating a redemption request yet. (we need at least {} (configurable))",
verified_tickets.len(),
self.config.minimum_redemption_tickets
);
return Ok(());
}
} else {
// first proposal
debug!(
"we only have {} verified tickets. there's no point in creating a redemption request yet. (we need at least {} (configurable))",
verified_tickets.len(),
self.config.minimum_redemption_tickets
);
return Ok(());
}
}
// this should have been ensured when querying
assert!(verified_tickets.len() <= u16::MAX as usize);
let digest =
BatchRedeemTicketsBody::make_digest(verified_tickets.iter().map(|t| &t.serial_number));
let encoded_digest = bs58::encode(&digest).into_string();
let prior_proposal_id = if let Some(prior_proposal) = prior_proposal {
if prior_proposal.description == encoded_digest {
info!("we have already created proposal for those tickets");
Some(prior_proposal.id)
} else {
warn!(
"our missed proposal seem to have been for different tickets - abandoning it"
);
None
}
} else {
None
};
// if the proposal has already existed on chain, do use it. otherwise create a new one
let proposal_id = if let Some(prior) = prior_proposal_id {
prior
} else {
self.create_redemption_proposal(&digest, verified_tickets.len() as u16)
.await?
};
if proposal_id > u32::MAX as u64 {
// realistically will we ever reach it? no.
panic!(
"we have created more than {} proposals. we can't handle that.",
u32::MAX
)
}
self.shared_state
.storage
.insert_redemption_proposal(
&verified_tickets,
proposal_id as u32,
OffsetDateTime::now_utc(),
)
.await?;
let current_epoch = self.shared_state.current_epoch_id().await?;
let api_clients = self.shared_state.api_clients(current_epoch).await?;
let ids = api_clients.iter().map(|c| c.node_id).collect();
let mut pending = PendingRedemptionVote::new(
proposal_id,
digest,
verified_tickets
.into_iter()
.map(|t| t.serial_number)
.collect(),
current_epoch,
ids,
);
let resolution = self
.try_resolve_pending_proposal(&mut pending, Some(api_clients))
.await?;
if resolution.is_pending() {
warn!(
"failed to reach quorum for proposal {proposal_id}. apis: {:?} haven't responded. we'll retry later",
pending.pending
);
self.pending_redemptions.push(pending);
} else {
self.shared_state
.storage
.clear_post_proposal_data(
proposal_id as u32,
OffsetDateTime::now_utc(),
resolution.is_rejected(),
)
.await?;
}
Ok(())
}
async fn periodic_operations(&mut self) -> Result<(), EcashTicketError> {
trace!(
"attempting to resolve all pending operations -> tickets that are waiting for verification and possibly redemption"
"attempting to resolve all pending operations -> tickets that are waiting for verification"
);
// 1. retry all operations that have failed in the past: verification requests and pending redemption
// retry the pending verification requests that have failed before
self.resolve_pending().await?;
// 2. if applicable, attempt to redeem all newly verified tickets
self.maybe_redeem_tickets().await?;
Ok(())
}
@@ -7,7 +7,7 @@ use std::future::Future;
use std::ops::Deref;
use tokio::sync::RwLockReadGuard;
pub(crate) fn apis_stream<'a>(
pub fn apis_stream<'a>(
// if needed we could make this argument more generic to accept either locks or iterators, etc.
all_clients: &'a RwLockReadGuard<'a, Vec<EcashApiClient>>,
filter_by_id: &'a [u64],
@@ -22,7 +22,7 @@ pub(crate) fn apis_stream<'a>(
)
}
pub(crate) async fn for_each_api_concurrent<'a, F, Fut>(
pub async fn for_each_api_concurrent<'a, F, Fut>(
all_clients: &'a RwLockReadGuard<'a, Vec<EcashApiClient>>,
filter_by_id: &'a [u64],
f: F,
@@ -20,7 +20,7 @@ use tracing::error;
pub mod credential_sender;
pub mod error;
mod helpers;
pub mod helpers;
mod state;
pub mod traits;
@@ -3,17 +3,12 @@
use crate::Error;
use crate::ecash::error::EcashTicketError;
use cosmwasm_std::{CosmosMsg, WasmMsg, from_json};
use nym_credentials_interface::VerificationKeyAuth;
use nym_ecash_contract_common::msg::ExecuteMsg;
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::AccountId;
use nym_validator_client::nyxd::contract_traits::{
DkgQueryClient, MultisigQueryClient, NymContractsProvider,
};
use nym_validator_client::nyxd::cw3::ProposalResponse;
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, NymContractsProvider};
use nym_validator_client::{DirectSigningHttpRpcNyxdClient, EcashApiClient};
use std::collections::BTreeMap;
use std::ops::Deref;
@@ -77,53 +72,6 @@ impl SharedState {
Ok(this)
}
fn created_redemption_proposal(&self, proposal: &ProposalResponse) -> bool {
let Some(msg) = proposal.msgs.first() else {
return false;
};
let CosmosMsg::Wasm(WasmMsg::Execute { msg, .. }) = msg else {
return false;
};
let Ok(ExecuteMsg::RedeemTickets { gw, .. }) = from_json(msg) else {
return false;
};
gw == self.address.as_ref()
}
/// retrieve all redemption proposals made by this gateway since, but excluding, the provided id
pub(crate) async fn proposals_since(
&self,
proposal_id: u64,
) -> Result<Vec<ProposalResponse>, EcashTicketError> {
Ok(self
.start_query()
.await
.list_proposals(Some(proposal_id), None)
.await
.map_err(EcashTicketError::chain_query_failure)?
.proposals
.into_iter()
.filter(|p| self.created_redemption_proposal(p))
.collect())
}
/// retrieve all redemption proposals made by this gateway that are available on the last page of the query
pub(crate) async fn last_proposal_page(
&self,
) -> Result<Vec<ProposalResponse>, EcashTicketError> {
Ok(self
.start_query()
.await
.reverse_proposals(None, None)
.await
.map_err(EcashTicketError::chain_query_failure)?
.proposals
.into_iter()
.filter(|p| self.created_redemption_proposal(p))
.collect())
}
async fn set_epoch_data(
&self,
epoch_id: EpochId,
@@ -240,24 +188,6 @@ impl SharedState {
data.get(&epoch_id).map(|d| &d.master_key).unwrap()
}))
}
pub(crate) async fn start_tx(&self) -> RwLockWriteGuard<'_, DirectSigningHttpRpcNyxdClient> {
self.nyxd_client.write().await
}
pub(crate) async fn start_query(&self) -> RwLockReadGuard<'_, DirectSigningHttpRpcNyxdClient> {
self.nyxd_client.read().await
}
pub(crate) async fn current_epoch_id(&self) -> Result<EpochId, EcashTicketError> {
Ok(self
.start_query()
.await
.get_current_epoch()
.await
.map_err(EcashTicketError::chain_query_failure)?
.epoch_id)
}
}
pub(crate) struct EpochState {
+6 -3
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-credentials-interface"
description = "Interface for Nym's compact eacash / zknym credential scheme"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Interface for Nym's compact eacash / zknym credential scheme"
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+5 -1
View File
@@ -1,12 +1,16 @@
[package]
name = "nym-credentials"
description = "Crate for using Nym's zknym credentials"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Crate for using Nym's zknym credentials"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+7 -2
View File
@@ -1,11 +1,16 @@
[package]
name = "nym-crypto"
version.workspace = true
description = "Crypto library for the nym mixnet"
edition = { workspace = true }
version.workspace = true
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
[dependencies]
aes-gcm-siv = { workspace = true, optional = true }
+8 -4
View File
@@ -1,13 +1,17 @@
[package]
name = "nym-dkg"
version.workspace = true
edition = "2021"
resolver = "2"
license.workspace = true
description = "Nym's Distributed Key Generation functionality"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
resolver = "2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -3
View File
@@ -1,15 +1,16 @@
[package]
name = "nym-ecash-signer-check-types"
description = "Crate containing types for the `ecash-signer-check` crate used to check if zknym signers are up and running properly"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
description = "Crate containing types for the `ecash-signer-check` crate used to check if zknym signers are up and running properly"
publish = true
[dependencies]
semver = { workspace = true }
+4 -3
View File
@@ -1,15 +1,16 @@
[package]
name = "nym-ecash-signer-check"
description = "Functions to interact with zknym signers, checking their status and health"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
description = "Functions to interact with zknym signers, checking their status and health"
publish = true
[dependencies]
futures = { workspace = true }
+6 -3
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-ecash-time"
description = "Time-related helper functions for Nym's zknym scheme"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Time-related helper functions for Nym's zknym scheme"
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+6 -3
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-exit-policy"
description = "Get and set the Nym Exit Policy, used by Exit Gateways"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Get and set the Nym Exit Policy, used by Exit Gateways"
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -1
View File
@@ -3,14 +3,17 @@
[package]
name = "nym-gateway-requests"
description = "Request and response definitions for Nym Gateway <> client communication"
version.workspace = true
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
description = "Request and response definitions for Nym Gateway <> client communication"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+5 -3
View File
@@ -1,14 +1,16 @@
[package]
name = "nym-gateway-stats-storage"
description = "Functionality Nym Gateway statistics storage"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
description = "Functionality Nym Gateway statistics storage"
readme.workspace = true
publish = true
[dependencies]
sqlx = { workspace = true, features = [
+5 -3
View File
@@ -1,14 +1,16 @@
[package]
name = "nym-gateway-storage"
description = "Crate handling db setup and use for Nym Gateways, used for credentials, packets, connections"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
description = "Crate handling db setup and use for Nym Gateways, used for credentials, packets, connections"
readme.workspace = true
publish = true
[dependencies]
async-trait = { workspace = true }
+4 -3
View File
@@ -1,15 +1,16 @@
[package]
name = "nym-http-api-client-macro"
description = "Proc-macros for configuring HTTP clients globally via the `inventory` crate"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
description = "Proc-macros for configuring HTTP clients globally via the `inventory` crate"
publish = true
[lib]
proc-macro = true
+7 -3
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-http-api-client"
description = "Nym's HTTP API client, examples, and tests"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Nym's HTTP API client, examples, and tests"
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -34,6 +37,7 @@ tracing = { workspace = true }
itertools = { workspace = true }
inventory = { workspace = true }
tokio = { workspace = true, features = ["rt", "macros", "time"] }
rustls = { workspace=true }
# used for decoding text responses (they were already implicitly included)
bytes = { workspace = true }
encoding_rs = { workspace = true }
+1 -1
View File
@@ -4,7 +4,7 @@ use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
pub const NYM_API_DOMAIN: &str = "validator.nymtech.net";
pub const NYM_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(212, 71, 233, 232))];
pub const NYM_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(92, 39, 63, 14))];
pub const NYM_VPN_API_DOMAIN: &str = "nymvpn.com";
pub const NYM_VPN_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(76, 76, 21, 21))];
+65 -5
View File
@@ -161,6 +161,8 @@ use reqwest::{RequestBuilder, Response};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
#[cfg(not(target_arch = "wasm32"))]
use std::io::ErrorKind;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use thiserror::Error;
@@ -1167,14 +1169,10 @@ impl ApiClientCore for Client {
match response {
Ok(resp) => return Ok(resp),
Err(err) => {
// only if there was a network issue should we consider updating the host info
//
// note: for now this includes DNS resolution failure, I am not sure how I would go about
// segregating that based on the interface provided by request for errors.
#[cfg(target_arch = "wasm32")]
let is_network_err = err.is_timeout();
#[cfg(not(target_arch = "wasm32"))]
let is_network_err = err.is_timeout() || err.is_connect();
let is_network_err = might_be_network_interference(&err);
if is_network_err {
// if we have multiple urls, update to the next
@@ -1222,6 +1220,68 @@ impl ApiClientCore for Client {
}
}
#[cfg(not(target_arch = "wasm32"))]
const MAX_ERR_SOURCE_ITERATIONS: usize = 4;
/// This functions attempts to check the error returned by reqwest to see if
/// rotating host informtion (for clients with mutliple hosts defined) could be
/// helpful. This looks for situations where the error could plausibly be caused
/// by a network adversary, or where rotating to an equival hostname might help.
///
/// For example --> NetworkUnreachable will not be helped by rotating domains,
/// but ConnectionReset might be caused by a network adversary blocking by SNI
/// which could possibly benefit from rotating domains.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn might_be_network_interference(err: &reqwest::Error) -> bool {
if err.is_timeout() {
return true;
}
if !(err.is_connect() || err.is_request()) {
return false;
}
// The io::Error source is several layers deep, for clarity this is done as a loop
// * reqwest::Error -> hyper_util::Error
// * hyper_util::Error -> hyper_util::ClientError
// * hyper_util::ClientError -> io::Error
let mut inner = err.source();
for _ in 0..MAX_ERR_SOURCE_ITERATIONS {
if let Some(e) = inner {
if let Some(io_err) = e.downcast_ref::<std::io::Error>() {
// try downcast to io::Error from <dyn std::error:Error>
match io_err.kind() {
// device not connected to the internet
ErrorKind::NetworkUnreachable | ErrorKind::NetworkDown => return false,
// connection errors can indicate connection interference
ErrorKind::ConnectionReset
| ErrorKind::HostUnreachable
| ErrorKind::ConnectionRefused => return true,
// TLS errors get wrapped in custom io::Errors
ErrorKind::Other | ErrorKind::InvalidData => {
// io::Error get_ref works while source doesn't here -_-
// if you don't like it take it up with the rust devs https://users.rust-lang.org/t/question-about-implementation-of-std-source/121117
inner = io_err.get_ref().map(|e| e as &dyn std::error::Error);
}
_ => return false,
}
} else if let Some(_tls_err) = e.downcast_ref::<rustls::Error>() {
// try downcast to TLS error
return true;
} else if let Some(resolve_err) = e.downcast_ref::<hickory_resolver::ResolveError>() {
// try downcast to DNS error
return resolve_err.is_nx_domain();
} else {
inner = e.source();
}
} else {
break;
}
}
false
}
/// Common usage functionality for the http client.
///
/// These functions allow for cleaner downstream usage free of type parameters and unneeded imports.
+6 -3
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-http-api-common"
description = "Common crate for Nym-related HTTP API interaction"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Common crate for Nym-related HTTP API interaction"
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+7 -2
View File
@@ -1,11 +1,16 @@
[package]
name = "nym-inclusion-probability"
version.workspace = true
description = "Nym active set probability simulator"
edition.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
[dependencies]
log = { workspace = true }
+12 -3
View File
@@ -1,16 +1,23 @@
[package]
name = "nym-ip-packet-requests"
description = "Codec, signing functionality, and different version definitions for IP packet request and responses"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Codec, signing functionality, and different version definitions for IP packet request and responses"
rust-version.workspace = true
readme.workspace = true
publish = true
[features]
test-utils = ["pnet_packet"]
[dependencies]
pnet_packet = { workspace = true, optional = true }
bincode = { workspace = true }
bytes = { workspace = true }
nym-bin-common = { workspace = true }
@@ -18,8 +25,10 @@ nym-crypto = { workspace = true }
nym-service-provider-requests-common = { workspace = true }
nym-sphinx = { workspace = true }
rand = { workspace = true }
semver = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
time = { workspace = true }
tokio = { workspace = true, features = ["time"] }
tokio-util = { workspace = true, features = ["codec"] }
tracing = { workspace = true }
+117
View File
@@ -0,0 +1,117 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// Extracted from sdk/rust/nym-sdk/examples/ipr_tunnel.rs
//! ICMP/ICMPv6 packet construction and reply detection helpers for testing
//! IPR connectivity. Gated behind the `test-utils` feature.
use std::net::{Ipv4Addr, Ipv6Addr};
use pnet_packet::Packet;
use pnet_packet::icmp::echo_reply::EchoReplyPacket;
use pnet_packet::icmp::echo_request::MutableEchoRequestPacket;
use pnet_packet::icmp::{IcmpPacket, IcmpTypes};
use pnet_packet::icmpv6::Icmpv6Types;
use pnet_packet::ipv4::{Ipv4Flags, MutableIpv4Packet};
use pnet_packet::ipv6::MutableIpv6Packet;
/// Build a complete IPv4 ICMP echo request packet.
pub fn build_icmp_ping(src: Ipv4Addr, dst: Ipv4Addr, seq: u16) -> Option<Vec<u8>> {
let mut echo = MutableEchoRequestPacket::owned(vec![0u8; 64])?;
echo.set_icmp_type(IcmpTypes::EchoRequest);
echo.set_icmp_code(pnet_packet::icmp::IcmpCode::new(0));
echo.set_sequence_number(seq);
let cksum = pnet_packet::icmp::checksum(&IcmpPacket::new(echo.packet())?);
echo.set_checksum(cksum);
let total_len = 20 + echo.packet().len();
let mut ip = MutableIpv4Packet::owned(vec![0u8; total_len])?;
ip.set_version(4);
ip.set_header_length(5);
ip.set_total_length(total_len as u16);
ip.set_ttl(64);
ip.set_next_level_protocol(pnet_packet::ip::IpNextHeaderProtocols::Icmp);
ip.set_source(src);
ip.set_destination(dst);
ip.set_flags(Ipv4Flags::DontFragment);
ip.set_payload(echo.packet());
let mut buf = ip.consume_to_immutable().packet().to_vec();
let cksum = ipv4_checksum(&buf);
buf[10] = (cksum >> 8) as u8;
buf[11] = cksum as u8;
Some(buf)
}
/// Build a complete IPv6 ICMPv6 echo request packet.
pub fn build_icmpv6_ping(src: Ipv6Addr, dst: Ipv6Addr, seq: u16) -> Option<Vec<u8>> {
let mut echo =
pnet_packet::icmpv6::echo_request::MutableEchoRequestPacket::owned(vec![0u8; 64])?;
echo.set_icmpv6_type(Icmpv6Types::EchoRequest);
echo.set_icmpv6_code(pnet_packet::icmpv6::Icmpv6Code::new(0));
echo.set_sequence_number(seq);
let cksum = pnet_packet::icmpv6::checksum(
&pnet_packet::icmpv6::Icmpv6Packet::new(echo.packet())?,
&src,
&dst,
);
echo.set_checksum(cksum);
let payload_len = echo.packet().len();
let mut ip = MutableIpv6Packet::owned(vec![0u8; 40 + payload_len])?;
ip.set_version(6);
ip.set_payload_length(payload_len as u16);
ip.set_next_header(pnet_packet::ip::IpNextHeaderProtocols::Icmpv6);
ip.set_hop_limit(64);
ip.set_source(src);
ip.set_destination(dst);
ip.set_payload(echo.packet());
Some(ip.consume_to_immutable().packet().to_vec())
}
/// Check if a raw packet is an IPv4 ICMP echo reply destined to `expected_dst`.
pub fn is_echo_reply_v4(data: &[u8], expected_dst: Ipv4Addr) -> bool {
let Some(ip) = pnet_packet::ipv4::Ipv4Packet::new(data) else {
return false;
};
if ip.get_destination() != expected_dst {
return false;
}
if ip.get_next_level_protocol() != pnet_packet::ip::IpNextHeaderProtocols::Icmp {
return false;
}
let Some(reply) = EchoReplyPacket::new(ip.payload()) else {
return false;
};
reply.get_icmp_type() == IcmpTypes::EchoReply
}
/// Check if a raw packet is an IPv6 ICMPv6 echo reply destined to `expected_dst`.
pub fn is_echo_reply_v6(data: &[u8], expected_dst: Ipv6Addr) -> bool {
let Some(ip) = pnet_packet::ipv6::Ipv6Packet::new(data) else {
return false;
};
if ip.get_destination() != expected_dst {
return false;
}
if ip.get_next_header() != pnet_packet::ip::IpNextHeaderProtocols::Icmpv6 {
return false;
}
let Some(reply) = pnet_packet::icmpv6::echo_reply::EchoReplyPacket::new(ip.payload()) else {
return false;
};
reply.get_icmpv6_type() == Icmpv6Types::EchoReply
}
fn ipv4_checksum(header: &[u8]) -> u16 {
let mut sum = 0u32;
for i in (0..20).step_by(2) {
sum += ((header[i] as u32) << 8) | header[i + 1] as u32;
}
while (sum >> 16) > 0 {
sum = (sum & 0xFFFF) + (sum >> 16);
}
!sum as u16
}
+6
View File
@@ -3,10 +3,14 @@ use std::fmt::{Display, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
pub mod codec;
#[cfg(feature = "test-utils")]
pub mod icmp_utils;
pub mod response_helpers;
pub mod sign;
pub mod v6;
pub mod v7;
pub mod v8;
pub mod v9;
// version 3: initial version
// version 4: IPv6 support
@@ -14,6 +18,8 @@ pub mod v8;
// version 6: Increase the available IPs
// version 7: Add signature support (for the future)
// version 8: Anonymous sends
// version 9: LP-framed transport (SphinxStream)
// response_helpers: shared IPR response parsing (nym-ip-packet-client + nym-sdk)
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
@@ -0,0 +1,134 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bytes::{Bytes, BytesMut};
use tokio_util::codec::Decoder;
use tracing::{error, info, warn};
use crate::{
IpPair,
codec::MultiIpPacketCodec,
v8::response::{
ConnectResponseReply, ControlResponse, InfoLevel, IpPacketResponse, IpPacketResponseData,
},
};
#[derive(Debug, thiserror::Error)]
pub enum IprResponseError {
#[error("no version byte in message")]
NoVersionByte,
#[error("version mismatch: received v{received}, expected v{expected}")]
VersionMismatch { expected: u8, received: u8 },
#[error("expected control response, got {0:?}")]
UnexpectedResponse(IpPacketResponseData),
#[error("connect denied: {0:?}")]
ConnectDenied(crate::v8::response::ConnectFailureReason),
}
pub enum MixnetMessageOutcome {
IpPackets(Vec<Bytes>),
Disconnect,
}
// Extracted from:
// nym-ip-packet-client/src/helpers.rs — check_ipr_message_version()
// sdk/rust/nym-sdk/src/ip_packet_client/listener.rs — check_ipr_message_version()
/// Check that the first byte of an IPR message matches the expected protocol version.
pub fn check_ipr_message_version(data: &[u8], expected: u8) -> Result<(), IprResponseError> {
let version = data.first().ok_or(IprResponseError::NoVersionByte)?;
if *version != expected {
return Err(IprResponseError::VersionMismatch {
expected,
received: *version,
});
}
Ok(())
}
// Extracted from:
// nym-ip-packet-client/src/connect.rs — handle_connect_response() + handle_ip_packet_router_response()
// sdk/rust/nym-sdk/src/ip_packet_client/discovery.rs — parse_connect_response()
/// Parse an IPR connect response, returning allocated IPs on success.
pub fn parse_connect_response(response: IpPacketResponse) -> Result<IpPair, IprResponseError> {
let control_response = match response.data {
IpPacketResponseData::Control(c) => c,
other => return Err(IprResponseError::UnexpectedResponse(other)),
};
match *control_response {
ControlResponse::Connect(connect_resp) => match connect_resp.reply {
ConnectResponseReply::Success(success) => Ok(success.ips),
ConnectResponseReply::Failure(reason) => Err(IprResponseError::ConnectDenied(reason)),
},
_ => Err(IprResponseError::UnexpectedResponse(
IpPacketResponseData::Control(control_response),
)),
}
}
// Extracted from:
// nym-ip-packet-client/src/listener.rs — IprListener::handle_reconstructed_message()
// sdk/rust/nym-sdk/src/ip_packet_client/listener.rs — handle_ipr_response()
/// Parse raw IPR response bytes into an outcome.
///
/// Logs non-fatal conditions (unknown control messages, deserialization
/// failures) and returns `None` for them.
pub fn handle_ipr_response(data: &[u8]) -> Option<MixnetMessageOutcome> {
match IpPacketResponse::from_bytes(data) {
Ok(response) => match response.data {
IpPacketResponseData::Data(data_response) => {
let mut codec = MultiIpPacketCodec::new();
let mut buf = BytesMut::from(data_response.ip_packet.as_ref());
let mut packets = Vec::new();
loop {
match codec.decode(&mut buf) {
Ok(Some(packet)) => packets.push(packet.into_bytes()),
Ok(None) => break,
Err(e) => {
warn!("Failed to decode bundled IP packet: {e}");
break;
}
}
}
Some(MixnetMessageOutcome::IpPackets(packets))
}
IpPacketResponseData::Control(control_response) => match *control_response {
ControlResponse::Connect(_) => {
info!("Received connect response when already connected - ignoring");
None
}
ControlResponse::Disconnect(_) | ControlResponse::UnrequestedDisconnect(_) => {
info!("Received disconnect from IPR");
Some(MixnetMessageOutcome::Disconnect)
}
ControlResponse::Pong(_) => {
info!("Received pong response");
None
}
ControlResponse::Health(_) => {
info!("Received health response");
None
}
ControlResponse::Info(info_resp) => {
let msg = format!(
"Received info response from the mixnet: {}",
info_resp.reply
);
match info_resp.level {
InfoLevel::Info => info!("{msg}"),
InfoLevel::Warn => warn!("{msg}"),
InfoLevel::Error => error!("{msg}"),
}
None
}
},
},
Err(err) => {
warn!("Failed to deserialize IPR response: {err}");
None
}
}
}
+6 -2
View File
@@ -179,11 +179,15 @@ impl IpPacketResponse {
make_bincode_serializer().serialize(self)
}
pub fn from_bytes(data: &[u8]) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(data)
}
pub fn from_reconstructed_message(
message: &nym_sphinx::receiver::ReconstructedMessage,
) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(&message.message)
Self::from_bytes(&message.message)
}
}
+34
View File
@@ -0,0 +1,34 @@
pub const VERSION: u8 = 9;
/// Minimum nym-node release version that supports v9 (LP Stream framing).
/// Nodes running older versions will not understand LP-wrapped packets.
pub const MIN_RELEASE_VERSION: semver::Version = semver::Version::new(1, 30, 0);
// v9 uses the same wire format as v8. The version bump indicates
// the message was sent with LP framing (SphinxStream).
//
// Types are re-exported for deserialization/matching. Use the wrapper
// constructors below to create correctly-versioned packets — never
// manually set `protocol.version` or `response.version`.
pub use super::v8::{request, response};
/// Create a v9 connect request (version byte set to 9).
pub fn new_connect_request(buffer_timeout: Option<u64>) -> (request::IpPacketRequest, u64) {
let (mut req, id) = request::IpPacketRequest::new_connect_request(buffer_timeout);
req.protocol.version = VERSION;
(req, id)
}
/// Create a v9 data request (version byte set to 9).
pub fn new_data_request(data: bytes::Bytes) -> request::IpPacketRequest {
let mut req = request::IpPacketRequest::new_data_request(data);
req.protocol.version = VERSION;
req
}
/// Create a v9 IP packet response (version byte set to 9).
pub fn new_ip_packet_response(ip_packet: bytes::Bytes) -> response::IpPacketResponse {
let mut resp = response::IpPacketResponse::new_ip_packet(ip_packet);
resp.version = VERSION;
resp
}
+4 -1
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-mixnode-common"
description = "Common crate for Nym Mix Nodes"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
description = "Common crate for Nym Mix Nodes"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+7 -2
View File
@@ -1,11 +1,16 @@
[package]
name = "nym-network-defaults"
version.workspace = true
description = "Nym network defaults"
edition.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# Exclude build.rs from published crate - it's only used for dev-time sync
# of env files and requires workspace context
exclude = ["build.rs"]
+5 -1
View File
@@ -1,12 +1,16 @@
[package]
name = "nym-node-tester-utils"
description = "Utils for the Nym Node Tester"
version.workspace = true
authors.workspace = true
edition = "2021"
license.workspace = true
description = "Utils for the Nym Node Tester"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -1
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-nonexhaustive-delayqueue"
description = "A copy of tokio-util delay_queue with `Sleep` and `Instant` being replaced with`wasm_timer` equivalents"
version.workspace = true
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
description = "A copy of tokio-util delay_queue with `Sleep` and `Instant` being replaced with`wasm_timer` equivalents"
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -3
View File
@@ -1,15 +1,16 @@
[package]
name = "nym-cache"
description = "Helper functions around a RwLock for writing to local cache of items"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
description = "Helper functions around a RwLock for writing to local cache of items"
publish = true
[dependencies]
tokio = { workspace = true, features = ["sync"] }
+8 -3
View File
@@ -1,11 +1,16 @@
[package]
name = "nym-common"
description = "Runtime diagnostics for high frequency logging, debugging and error handling utilities"
version.workspace = true
authors.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
description = "Runtime diagnostics for high frequency logging, debugging and error handling utilities"
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme.workspace = true
publish = true
[lints]
workspace = true
@@ -1,3 +1,6 @@
// Copyright 2026 Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::{self, Write};
pub fn format_debug_bytes(bytes: &[u8]) -> Result<String, fmt::Error> {
+1
View File
@@ -2,6 +2,7 @@
// Copyright 2024 Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
pub mod debug;
mod error;
pub mod flood;
+2 -2
View File
@@ -2,11 +2,11 @@
name = "nym-connection-monitor"
version = "1.18.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
publish = false
[lints]
+6 -3
View File
@@ -1,13 +1,16 @@
[package]
name = "nym-id"
description = "Functionality for importing and storing credentials and cryptographic keys"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
description = "Functionality for importing and storing credentials and cryptographic keys"
rust-version.workspace = true
readme.workspace = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

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