Compare commits

...

146 Commits

Author SHA1 Message Date
benedettadavico db64706a1f testing quorum 2025-11-13 17:23:10 +01:00
Drazen Urch aac983d922 Remove debug feature from http-macro spec in gateway probe (#6195) 2025-11-13 14:18:29 +01:00
mfahampshire 577675bab3 Remove old conceptsoverview page + move index to proper place in sidebar (#6196) 2025-11-13 11:38:54 +00:00
mfahampshire ec015618cd update gw probe to point @ monorepo (#6194)
* update gw probe to point @ monorepo

* add funded nyx account info
2025-11-13 11:02:45 +00:00
mfahampshire fa40acbeca fixed broken link (#6193) 2025-11-12 15:12:38 +00:00
import this 386e1790dd [DOCs/operators]: Release notes for v2025.20 leerdammer (#6191)
* release notes

* bump up nym-node docs version

* add dev tools

* scrape stats and clean
2025-11-12 12:32:13 +00:00
mfahampshire d07f9c8fad Max/docs new structure (#6188)
* rework of sdk docs

* update integration docs + bit of overall restructure

* remove debug logger from tool
2025-11-12 11:03:28 +00:00
Tommy Verrall 0dc071daeb Merge pull request #6179 from nymtech/dns-debug
DNS relibility and troubleshooting
2025-11-12 11:01:20 +01:00
jmwample 10951d4cd3 clippy, fmt, minor fix 2025-11-11 10:40:25 -07:00
mfahampshire 872c25bfcc Use hardcoded devrel gw for examples to get around CSP (#6187)
* Use hardcoded devrel gw for examples to get around CSP

* remove comment
2025-11-11 16:22:41 +00:00
jmwample 5acce42c64 add some staic hosts and switch server strategy 2025-11-11 09:14:26 -07:00
mfahampshire 4848d081d0 Max/tweak ts sdk actions (#6185)
* add taskset to wasm release build commands

* bump taskset cores
2025-11-11 10:19:55 +00:00
mfahampshire b3452ede77 add wss to prod csp (#6183)
* add wss to csp
2025-11-10 20:48:02 +00:00
import this a44819b14c [DOCs/operators]: Cleanup (#6184)
* cleanup

* add ipr abbrs

* syntax fix

* syntax fix

* fix link path

* QUIC non-root warning

* syntax fix

* update stats

* address comment

* fix url path
2025-11-10 14:20:15 +00:00
mfahampshire 5455110810 removed warning from TSSDK (#6182)
* removed warning from TSSDK

* tweak
2025-11-10 12:21:20 +00:00
Mark Sinclair a0178d28f7 Typescript SDK 1.4.1 (#6146)
* wasm: mix-fetch: remove harbour master client and use Nym API client

* wasm: mix-fetch: fix up internal tester

* Release Typescript SDK v1.4.1

* remove bump version tool from workspace

* ts-sdk: contract clients: update and re-run autogen

* ts: fix linting errors

* update go

* pin minimatch typings to fix lint errors

* bump versions to rc

* Update publish-sdk-npm.yml

* Update publish-sdk-npm.yml

* Update publish-sdk-npm.yml

* Update publish-sdk-npm.yml

* try disable typedoc because of minimatch errors

* bump versions to rc0

* limit packages published to only wasm clients

* TS SDK 1.4.1-rc1

* simplify version dependencies and add dist to dev mode

* add back version complexity for CI

* TS SDK 1.4.1-rc2

* ts-sdk: fix minimatch dependency and correct casing on `selfAddress` function call

* wasm: rename `main` to `main_js` to avoid collision errors in exporting main from tests

see https://github.com/wasm-bindgen/wasm-bindgen/issues/2206

* improve wasm js tests

* TS SDK 1.4.1-rc3

* update example docs

* TS SDK 1.4.1 release

* update docs dependencies to SDK 1.4.1

* update yarn lock file after TS SDK 1.4.1 publish

* Update ci-lint-typescript.yml

* Update ci-lint-typescript.yml

* Update ci-nym-wallet-storybook.yml

* Bump node tester version and add additional yarn install step to fix linting

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-11-07 21:17:42 +00:00
import this 3e42160426 [Docs/operators]: Performance measurement pages (#6177)
* initialise wg perf docs

* fix paths to absolute

* fix paths to absolute

* vpn coloring guide

* improve quic script dwl way

* refactor operators menu structure

* create probe-details page

* wg syntax change

* modified time

* fix link paths

* remove redundancy

* fix comments & bump stats
2025-11-07 15:41:59 +00:00
jmwample 2f752a6c42 fix things related to interface changes 2025-11-06 18:37:50 -07:00
Jack Wampler 806f807f02 Implement Static DNS fallback (#6178) 2025-11-06 16:46:39 -07:00
Jack Wampler 1400db6156 DNS Reliability Fixes (#6175) 2025-11-06 12:37:27 -07:00
Simon Wicky 673a3e45d3 [bugfix] Distinguish authenticator errors by credential spent (#6176)
* distinguish authenticator errors by credential spent

* nitpicking fixes

* fix CI to see those changes

* error naming
2025-11-06 18:07:07 +01:00
Jędrzej Stuczyński d9c2f6ebda Feature/credential proxy jwt (#5957)
* squashed feature/credential-proxy-jwt [#5957]

post rebasing fixes

clippy

changed obtain-async endpoint to conditionally return jwt instead of pending zk-nym

watching for the attestation file and issuing jwt

* changed attestation starting time serialisation into rfc3339

* including authorised JWT issuers in attestation

* reduce attestation retrieval error log
2025-11-03 16:42:39 +00:00
import this e24e094711 [DOCs/operators]: Cleanup (#6170)
* fix quic docs steps

* update stats

* fix typo

* quic bridge update
2025-10-31 13:33:24 +00:00
Jędrzej Stuczyński 378f32e6d7 disconnect mixnet client if registration fails (#6169)
Co-authored-by: Simon Wicky <simon@nymtech.net>
2025-10-31 12:07:22 +00:00
Jędrzej Stuczyński 3e9b8d237f Merge pull request #6168 from nymtech/chore/clippy-1.91
chore: resolve clippy 1.91 warnings
2025-10-31 12:00:55 +00:00
Jędrzej Stuczyński f5a4dbc555 removed useless use of vec! 2025-10-31 11:42:42 +00:00
Jędrzej Stuczyński 4480534e4d derived Default impl for EpochState 2025-10-31 11:39:58 +00:00
Jędrzej Stuczyński d355f9d752 Merge pull request #6167 from nymtech/master
master -> develop
2025-10-31 11:38:15 +00:00
Jędrzej Stuczyński 9f3a370496 Merge pull request #6166 from nymtech/merge/release/2025.19-kase-cherry-picked
release/2025.19 kase into master
2025-10-31 11:28:51 +00:00
Jędrzej Stuczyński e4adc5d954 Merge pull request #6165 from nymtech/release/2025.19-kase-cherry-picked
release/2025.19 kase into develop
2025-10-31 11:28:44 +00:00
Jędrzej Stuczyński 00373b70e2 Merge branch 'master' into merge/release/2025.19-kase-cherry-picked 2025-10-31 10:54:29 +00:00
benedettadavico 65f2017422 update changelog 2025-10-31 10:47:36 +00:00
benedettadavico 192f258463 update workflow 2025-10-31 10:47:34 +00:00
Tommy Verrall a5eee7444b Merge pull request #6143 from nymtech/bugfix/mix-tx-closed-v2
Bugfix: Add circuit breaker
2025-10-31 10:47:20 +00:00
benedettadavico 6abd7e7352 bump versions 2025-10-31 10:47:10 +00:00
Jędrzej Stuczyński 3306ca5357 merge 'master' into 'develop' 2025-10-30 17:43:42 +00:00
Jędrzej Stuczyński 9c2ccead0e Merge branch 'master' into merge/master/develop 2025-10-30 17:30:39 +00:00
import this b7aeb51362 [DOCs/operators]: Release notes for v2025.19 kase (#6157)
* add release and operators notes

* bump up version

* fix location in csv to USA

* bump up stats

* typo fix
2025-10-30 16:21:04 +00:00
benedetta davico e9e725defe Merge pull request #6093 from nymtech/bugfix/ns-api-node-custom-http-api-port
ns-api: fix scraping bug when operator specifies custom node HTTP API port in bond
2025-10-30 16:49:02 +01:00
import this c74494a21d [Feature/operators]: QUIC bridge deployment script v2 (#6145)
* new quick deployment script

* docs tweak

* update script to use .deb postinst

* final clean - ready to go

* correct nym-node config dir search with a fallback
2025-10-30 12:22:55 +00:00
Simon Wicky 54f6c98c22 remove unused deps (#6151) 2025-10-29 11:48:49 +01:00
Simon Wicky 846484bbb4 use typed builder (#6150) 2025-10-27 17:49:50 +01:00
Tommy Verrall fb3f5501ba Merge pull request #6143 from nymtech/bugfix/mix-tx-closed-v2
Bugfix: Add circuit breaker
2025-10-27 16:45:41 +01:00
Tommy Verrall e8a607f520 Merge pull request #6149 from nymtech/simon/tommy_too_quick
tommy is too quick
2025-10-27 13:52:55 +01:00
Simon Wicky f5f6df9eaf tommy is too quick 2025-10-27 13:50:49 +01:00
Tommy Verrall c647ab5605 Merge pull request #6148 from nymtech/simon/registration_client_timeout
configurable mixnet client startup timeout
2025-10-27 13:47:48 +01:00
Simon Wicky 416c21a42e configurable mixnet client startup timeout 2025-10-27 13:35:46 +01:00
Simon Wicky fd5a95fa4d allow overwriting existing sdk shutdown manager 2025-10-24 16:17:29 +02:00
Simon Wicky c61df79182 typo 2025-10-24 14:11:56 +02:00
Simon Wicky 08559a7660 calling for shutdown from the MixTrafficController 2025-10-24 14:07:15 +02:00
Jędrzej Stuczyński 6dce55a99b using same hierarchy of trackers for client shutdown control 2025-10-24 14:03:18 +02:00
Tommy Verrall bc0b89b31c Internal comments 2025-10-24 12:44:10 +02:00
Tommy Verrall 67c32faa11 Fix comments 2025-10-23 19:22:26 +02:00
Tommy Verrall aa0d15ee67 Better message to come in the PR description 2025-10-23 19:06:27 +02:00
p17o 4f0974fcf1 Update quic_bridge_deployment.sh for IPv4 and .deb package (#6138)
Updated ping commands to explicitly use IPv4 and adjusted file permission checks with sudo. Changed the forward address prompt to specify IPv4 and modified the binary download process to fetch and install the latest .deb release URL automatically.
2025-10-22 15:46:23 +00:00
Jędrzej Stuczyński bd2174641e bugfix: update internal owner address in transferred share (#6139) 2025-10-22 10:42:26 +01:00
Tommy Verrall 59b62fabc9 Merge pull request #6134 from nymtech/feature/domain-fronting-v2
Domain fronting
2025-10-22 11:08:21 +02:00
Tommy Verrall e6f4bae895 Last failing test - fix 2025-10-21 19:34:20 +02:00
Tommy Verrall d71742af32 Use explicit Vec<ApiUrl> handling in BaseClientBuilder
- Replace NymNetworkDetails with explicit API URL handling
- Fix deprecated from_network() usage and improve fallback logic
- Add URL validation and remove unused backwards compatibility
2025-10-21 19:15:24 +02:00
Tommy Verrall 3b7c07e249 Actually commit the recommended changes 2025-10-21 18:12:38 +02:00
Tommy Verrall 3b429dde69 Fix broken tests in CI 2025-10-21 16:29:26 +02:00
Tommy Verrall 3a29c296da Replace deprecated from_network() with new_with_fronted_urls() 2025-10-21 16:05:41 +02:00
Tommy Verrall 8544c54f8f Merge develop into feature/domain-fronting-v2
- Use new_with_fronted_urls() for explicit domain fronting
- Deprecate from_network() in favor of explicit method
2025-10-21 15:58:20 +02:00
Jędrzej Stuczyński 9f9639950b feat: expose more explicit new_with_fronted_urls builder for http API client (#6136) 2025-10-21 14:47:58 +01:00
Jędrzej Stuczyński 111a0b20b6 bugfix: update stored epoch share when changing ownership (#6135) 2025-10-21 14:10:20 +01:00
Tommy Verrall 67b300d0b8 Fix new_from_env() to populate nym_api_urls for domain fronting 2025-10-21 12:22:51 +02:00
Jędrzej Stuczyński 88c4e0ce6c bugfix: update stored epoch share when changing announce address (#6131)
* bugfix: update stored epoch share when changing announce address

* chore: remove placeholder legacy mixnode bonding test [mixnet contract]
2025-10-21 10:59:17 +01:00
Tommy Verrall f6800aff0a fix all clippy messages 2025-10-21 11:32:47 +02:00
Tommy Verrall 0c7c927ca2 Add more tests for retry logic 2025-10-21 11:32:47 +02:00
Tommy Verrall a69c8b1660 Fix confusing tracing logs 2025-10-21 11:32:47 +02:00
Tommy Verrall f3ea310a46 Fix retries - this is working 2025-10-21 11:32:46 +02:00
Tommy Verrall 92f9ff035d Add configuration-based domain fronting support
Changes:
- Add network_details field to BaseClientBuilder (optional, backwards compatible)
- Add with_network_details() method for opt-in domain fronting
- Update construct_nym_api_client() to use from_network() when network_details provided
- Enable network-defaults feature in nym-client-core Cargo.toml
- SDK passes network_details to BaseClientBuilder
2025-10-21 11:32:46 +02:00
Tommy Verrall 5a817e1df1 Merge pull request #6126 from nymtech/multiple-fall-back-urls
Changes:

Multiple URL fallback with configurable retries (defaults to 3)
Infallible URL conversion per Andrews feedback (Url::from() instead of parse().ok())
Non-breaking builder pattern for BuilderConfig per Andrej's "too many arguments" feedback
Reverted redundant node filtering per Andrew's clarification that API already filters by supported_roles.entry
2025-10-21 11:27:37 +02:00
Tommy Verrall a07a24db00 Fix CI issues 2025-10-21 11:01:04 +02:00
Tommy Verrall a0cb812eff Allow clippy::enum_variant_names for BuilderConfigError 2025-10-21 10:35:57 +02:00
Tommy Verrall 923c1fa184 Improve error handling
Changes:
- Replace String error with BuilderConfigError enum in BuilderConfigBuilder
- Update tests to use pattern matching instead of string assertions
2025-10-20 16:57:31 +02:00
Tommy Verrall 35ea7e4926 - Add DEFAULT_NYM_API_RETRIES constant (replaces magic number 3)
- Run cargo fmt on all affected packages
- All clippy warnings resolved
2025-10-20 16:51:07 +02:00
Tommy Verrall d1cb9afaf0 not sure what happened but it's fixed 2025-10-20 15:20:24 +02:00
Tommy Verrall 79d4b4b2e3 Merge branch 'develop' into multiple-fall-back-urls 2025-10-20 15:16:36 +02:00
Tommy Verrall 8460b33946 Merge branch 'multiple-fall-back-urls' of https://github.com/nymtech/nym into multiple-fall-back-urls 2025-10-20 15:16:17 +02:00
Tommy Verrall ae6539e07c Merge resolution 2025-10-20 15:14:48 +02:00
Tommy Verrall 18cebdfedc Add accessor methods for Url internals
Add inner_url() and fronts() accessor methods to nym_http_api_client::Url
for VPN client integration
2025-10-20 14:33:57 +02:00
Tommy Verrall c448ec823a Remove tests for removed with_nym_api_client method
These tests were referencing with_nym_api_client() which was removed when
cleaning domain fronting code from this branch
2025-10-20 11:52:04 +02:00
Tommy Verrall a266137278 Add optional builder pattern for BuilderConfig (non-breaking)
Addresses @jstuczyn's feedback about too many arguments by adding
BuilderConfigBuilder as an alternative to the existing new() method.
2025-10-20 11:39:50 +02:00
Tommy Verrall 6f4dfd1dab fix conversion type && make the retry count configurable 2025-10-20 11:15:31 +02:00
Andy Duplain 57719787db Merge pull request #6130 from nymtech/andy/url_fronts
VPN-4262: Update `Url` to return `url` and `front` fields.
2025-10-17 15:44:08 +01:00
Andy Duplain 29a57bf172 VPN-4262: Update Url to return url and front fields.
The VPN client is using the `Url` type alot now and in order to avoid
double URL-parsing we would like the content of the `Url` type exposed.
2025-10-17 15:37:07 +01:00
Mark Sinclair 17d11f201e change migration and bump version 2025-10-17 15:04:53 +01:00
Mark Sinclair fef7e42eb4 bump version to rc 2025-10-17 14:56:02 +01:00
Mark Sinclair ceeeb6211b add tracing output 2025-10-17 14:52:59 +01:00
Mark Sinclair cd77b1032f clippy 2025-10-17 14:48:40 +01:00
Mark Sinclair 6bbb14f12f save custom_http_port to db 2025-10-17 14:48:40 +01:00
Mark Sinclair de8030d85a allow NS API to run once for scraping for troubleshooting and debugging 2025-10-17 14:48:40 +01:00
Mark Sinclair e18e64bf21 wip 2025-10-17 14:48:40 +01:00
Mark Sinclair a50c9ac3fb ns-api: fix scraping bug when operator specifies custom node HTTP API port in bond 2025-10-17 14:48:39 +01:00
Tommy Verrall db813b6e3e Revert node filtering changes per Andrew's feedback
Andrew clarified that get_basic_entry_assigned_nodes_v2() already filters by
supported_roles.entry
2025-10-17 15:18:28 +02:00
Tommy Verrall 1be5ba310a Remove domain fronting code to keep gateway changes only
This branch now contains only gateway registration improvements:
- Multiple URL fallback support in gateways_for_init()
- Get all entry-capable nodes for registration
- Performance and code quality improvements
2025-10-17 14:27:31 +02:00
Tommy Verrall 41ff3f7824 Address PR feedback: simplify code and reduce log noise
- Reverted all changes to topology_control/nym_api_provider.rs
- Changed info/warn logs to debug for custom client messages
- Removed unused _rng parameter from gateways_for_init()
- Simplified URL builder to always use new_with_urls()
2025-10-17 14:20:12 +02:00
Tommy Verrall c9d4d62446 Fix clippy warnings: use arrays instead of vec! in tests 2025-10-17 13:30:30 +02:00
Tommy Verrall e839a0d80e Merge develop into multiple-fall-back-urls
Resolved conflicts:
- Added event_tx field to MixnetClientBuilder alongside custom_nym_api_client
- Both features are independent and coexist:
  * custom_nym_api_client: for domain fronting support
  * event_tx: for event handling
- Updated all constructors and methods to properly handle both fields
2025-10-17 13:23:04 +02:00
Tommy Verrall cd61f930bf feat: pass custom HTTP client through SDK stack for domain fronting
- Add with_nym_api_client() to BaseClientBuilder, MixnetClientBuilder, and RegistrationClientBuilderConfig

- Modify nym_api_provider to fetch all nodes then filter by supported_roles.entry (fixes metadata inconsistency)

- Update helpers.rs to build HTTP client with all nym_apis URLs and retries for fallback support

- Fix SDK to use entry_capable_nodes() instead of entry_gateways() for broader gateway selection

This enables domain fronting and URL rotation throughout the entire SDK stack, improving censorship resistance and connection reliability. All changes are backward compatible - custom client is optional.
2025-10-17 08:36:23 +02:00
Bogdan-Ștefan Neacşu 0674f31227 Introduce event backchannel (#6119)
* Introduce even backchannel

* Rust fmt

* Rename Event to MixnetClientEvent

* Use unbounded_send for events

* Remove unused file

* Remove mut borrow

* Event hierarchy and mixnet client intermediary

* Export MixTrafficEvent in sdk
2025-10-16 19:02:36 +03:00
Jędrzej Stuczyński 3e4f563dce Merge pull request #6099 from nymtech/bugfix/incompatibility-fixes
Bugfix/incompatibility fixes
2025-10-16 15:58:43 +01:00
Tommy Verrall edcf2b1204 enable URL rotation and retries for mixnet gateway init 2025-10-16 16:22:57 +02:00
Jędrzej Stuczyński b07fb18113 Merge pull request #6125 from nymtech/merge/release/2025.18-jarlsberg
Merge/release/2025.18 jarlsberg
2025-10-16 14:50:16 +01:00
benedettadavico 017dea4afd update changelog 2025-10-16 14:09:46 +01:00
Jędrzej Stuczyński 5a9ce13beb Bugfix/bloomfilters purge (#6089)
* remove all old bloomfilters upon starting binary

* remove old bloomfilter file upon purging secondary data
2025-10-16 14:09:45 +01:00
benedettadavico 514cf25c68 bump versions 2025-10-16 13:53:06 +01:00
Andrej Mihajlov 49ee0636e4 Merge pull request #6109 from nymtech/am/update-dirs-6
Update dirs to 6.0
2025-10-16 12:59:31 +02:00
Jędrzej Stuczyński bb971ce99c bugfix: nym-credential-proxy query params parsing regression (#6121) 2025-10-16 11:40:12 +01:00
Tommy Verrall 54de369c1e Skip ipv6 metadata endpoint request (#6118)
Co-authored-by: Tommy Verrall <tommy@nymtech.net>
2025-10-16 11:39:53 +01:00
Jędrzej Stuczyński 6d6ce284df bugfix: revert some dep updates introduced in #6043 (#6120) 2025-10-16 11:39:09 +01:00
Andrej Mihajlov 56ad1c6c8e Merge pull request #6115 from nymtech/am/revert-cancel-token
Revert "Propagate cancel token to mixnet client"
2025-10-15 16:54:49 +02:00
Jędrzej Stuczyński 10b4a288c8 chore: restore pending dkg contract state migration (#6116)
since it has not yet been run on mainnet
2025-10-15 14:18:03 +01:00
benedetta davico bbbb9486ce Merge pull request #6117 from nymtech/probe/remove-1mb-file
update to no longer use 1mb files
2025-10-15 15:17:01 +02:00
benedetta davico 16e86e1a07 Update lib.go 2025-10-15 15:15:20 +02:00
Jędrzej Stuczyński ca0c9898f0 bugfix: retrieve and update ticketbook in the same query (#6101)
* bugfix: retrieve and update ticketbook in the same query

* bump up NS version

* Update Cargo.toml

* remove SKIP LOCKED part of the query

---------

Co-authored-by: benedetta davico <46782255+benedettadavico@users.noreply.github.com>
2025-10-15 13:53:07 +01:00
Andrej Mihajlov 8b73d4e615 Revert "Propagate cancel token to mixnet client"
This reverts commit 50a259d454.
2025-10-15 14:17:36 +02:00
mfahampshire 6a9a767ab4 DOCS Jarlsberg Release (#6111)
* First pass release notes

* build info
2025-10-15 09:20:03 +00:00
Andrej Mihajlov e03a9fa16f Merge pull request #6105 from nymtech/am/reg-client-mixnet-cancel-token
Propagate cancel token to mixnet client
2025-10-14 13:10:02 +02:00
Andrej Mihajlov a0fbd57d5b Update dirs to 6.0 2025-10-14 11:17:33 +02:00
mfahampshire 9856198356 Patch for operators to open wg metadata port (#6106) 2025-10-13 14:47:43 +00:00
Jędrzej Stuczyński 5c33846e57 bugfix: use custom topology provider for list of init gateways (#6092) 2025-10-13 12:01:51 +01:00
Andrej Mihajlov cfa7635ae1 Propagate cancel token to mixnet client 2025-10-13 12:25:54 +02:00
Jędrzej Stuczyński 5d45544c27 bugfix: include network name in the default gateway probe config path (#6100) 2025-10-13 10:05:54 +01:00
Jędrzej Stuczyński aa6a79cb3e feat: expose obtaining reference to Mnemonic from DirectSecp256k1HdWallet (#6083)
* feat: expose obtaining reference to Mnemonic from DirectSecp256k1HdWallet

* updated getters for stringified mnemonic
2025-10-13 09:22:15 +01:00
Georgio Nicolas b3a940770a Merge pull request #5919 from nymtech/georgio/dkg-fixes
Additional DKG Fixes
2025-10-10 17:47:11 +02:00
Mark Sinclair e980f76a81 ns-api: add descriptions to dVPN gateway responses (#6102)
* ns-api: add descriptions to dVPN gateway responses

* clippy

* fmt

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-10-10 15:40:18 +01:00
import this 9b38fef28f [DOCs/operators] QUIC deployment script & docs (#6098)
* add quic_bridge_deployment.sh

* create a snippet with quick install steps

* add quic deployment to changelog

* add quic deployment to node config page

* add version compatibility callout

* last edits and scraped stats update

* correct name of QUIC snippet

* fix naming

* fix naming

* re-run python-prebuild.sh aka time-now updated

* attempt to fix vercel build the hard way

* rerun npm

* build with pnpm

* restore lock file and rebuild w pnpm

* chore: update pnpm lockfile

* attempt to fix build

* attempt to fix runtime builds

* update ci-docs run OS
2025-10-10 14:38:37 +00:00
Mark Sinclair 43910ca635 Update ci-docs.yml 2025-10-10 15:00:25 +01:00
Mark Sinclair d3ccd7575a NS API: use new probe download filesize and milliseconds field (#6097)
* use milliseconds field

* change score thresholds

* bump to version 4.0.8

* NS API: adjust score categories (#6103)

* testing scores

* test version

* Update Cargo.toml

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
Co-authored-by: benedetta davico <46782255+benedettadavico@users.noreply.github.com>
2025-10-10 14:47:36 +01:00
Jędrzej Stuczyński 422f889df7 bugfix: testnet manager 02sql migration (#6096) 2025-10-10 09:38:45 +01:00
Jędrzej Stuczyński c9e96edc35 chore: remove unnecessary closure in 'calculate_score' inside node-status-api 2025-10-09 15:46:15 +01:00
benedetta davico 7768317046 Merge pull request #6095 from nymtech/bugfix/ns-api-download-filesize
ns-api: use download files size from probes instead of parsing filenames
2025-10-08 18:14:00 +02:00
Mark Sinclair 0ebbb1a540 ns-api: use download files size from probes instead of parsing filenames 2025-10-08 17:05:56 +01:00
Jędrzej Stuczyński 827c13b69e moved nym-gateway-probe to monorepo and updated rust-edition to 2024 (#6094)
dont build netstack in CI

additional rust 2024 fixes

fixes

removed temp.rs

first round of cleanup

removed duplicated NS types

moved gateway probe to the monorepo
2025-10-08 16:17:43 +01:00
Mark Sinclair 18ff09608c ns-api: add new fields for probe output for query_metadata and download file size and duration in ms (#6091)
Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-10-08 09:47:04 +01:00
Mark Sinclair 8cc996bc0d NS API: clamp load to offline when score is offline and add mixnet_score field to preformance_v2 (#6076)
* ns-api: when `score` is `Offline`, clamp `load` to `Offline`

* ns-api: bump version

* ns-api: add mixnet score field to performance_v2 struct

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-10-07 17:30:37 +01:00
mfahampshire 83a598907f Max/fix wasm client + build commands (#6043)
* Debug logging 

* Yield based logging

* Reintroduce non-dummy task manager, try add counting for
BatchMessageSender, a couple of compiler target introductions on use
statements.

* Fixed time runtime err

* Uncomment forgetme/rememberme

* remove diffs from debug

* missed commented out forgetme

* yet more forgetme comments

* * Added missing clientreqestsender clone to wasm client to stop
  premature drop & busyloop
* Removed hacky mem::forget fix

* Remove debug panic_hook

* Conditional import + use of wasm_utils::console_log

* add wasm_util dep

* Commenting out or removing debug logging

* Remove missed comment

* cleanup gitignore

* clippy

* update go version in ci

* removed unused deps

* add clippy ignore

* remove mixfetch from ci build

* add minifetch fix

* comment out unused ts builds

* stop contract clients killing ci for the moment

* wasm target locking for imports

* Either remove console_log! macro or introduce cfg(debug_assertions)

* downgrade netlink

* debug assertions for console_log import

* modify config logging (debug -> normal)

* remove clone for client_request_sender + grab directly in struct
  creation

* reintroduce debug print for config in debug mode

* remove ood / unused custom topology from worker example file

* clippy

* clippy - ignore todo() tests

* modified humantime test in line with new parsing rules
2025-10-07 09:55:41 +00:00
Georgio Nicolas bb06a1b7a8 Another offering for Clippy 2025-09-12 20:34:50 +02:00
Georgio Nicolas e783a5fced Offerings for clippy 2025-09-12 20:28:49 +02:00
Georgio Nicolas 8a24b45b5d Precompute BSGS table 2025-09-12 20:21:57 +02:00
Georgio Nicolas 10e4eba727 Use LazyLock to precompute generators 2025-08-08 19:14:37 +02:00
Georgio Nicolas 8ebf482f36 Fix clippy suggestion 2025-07-29 16:33:25 +02:00
Georgio Nicolas 6940ca427e Fix zeroization 2025-07-29 15:42:23 +02:00
Georgio Nicolas 24f877fda5 replace unsafe static values by function calls 2025-07-29 15:04:11 +02:00
766 changed files with 25582 additions and 17019 deletions
+13 -1
View File
@@ -8,10 +8,13 @@ on:
- 'gateway/**'
- 'integrations/**'
- 'nym-api/**'
- 'nym-authenticator-client/**'
- 'nym-credential-proxy/**'
- 'nym-ip-packet-client/**'
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-registration-client/**'
- 'nym-statistics-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
@@ -81,12 +84,21 @@ jobs:
command: fmt
args: --all -- --check
- name: Clippy
- name: Clippy (macos)
if: contains(matrix.os, 'mac')
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --exclude nym-gateway-probe -- -D warnings
- name: Clippy (non-macos)
if: contains(matrix.os, 'linux') || contains(matrix.os, 'windows')
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --lib --manifest-path contracts/Cargo.toml
args: --lib --manifest-path contracts/Cargo.toml --all-features
- name: Check formatting
uses: actions-rs/cargo@v1
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build:
runs-on: arc-ubuntu-22.04
runs-on: arc-linux-latest
env:
RUSTUP_PERMIT_COPY_RENAME: 1
defaults:
+10 -9
View File
@@ -6,16 +6,14 @@ on:
paths:
- "ts-packages/**"
- "sdk/typescript/**"
- "nym-connect/desktop/src/**"
- "nym-connect/desktop/package.json"
- "nym-wallet/src/**"
- "nym-wallet/package.json"
- "explorer/**"
- "explorer-v2/**"
- ".github/workflows/ci-lint-typescript.yml"
jobs:
build:
runs-on: ubuntu-22.04
runs-on: arc-linux-latest
env:
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
@@ -25,6 +23,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup yarn
run: npm install -g yarn
@@ -37,14 +36,12 @@ jobs:
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
run: cargo install wasm-opt
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "1.23.7"
go-version: "1.24.6"
- name: Install
run: yarn
@@ -52,7 +49,11 @@ jobs:
- name: Build packages
run: yarn build:ci
- name: Install again
run: yarn
- name: Lint
run: yarn lint
- name: Typecheck with tsc
run: yarn tsc
@@ -8,7 +8,7 @@ on:
jobs:
build:
runs-on: custom-linux
runs-on: arc-linux-latest
steps:
- uses: actions/checkout@v4
+6 -6
View File
@@ -4,10 +4,10 @@ on:
workflow_dispatch:
pull_request:
paths:
- 'wasm/**'
- 'clients/client-core/**'
- 'common/**'
- '.github/workflows/ci-sdk-wasm.yml'
- "wasm/**"
- "clients/client-core/**"
- "common/**"
- ".github/workflows/ci-sdk-wasm.yml"
jobs:
wasm:
@@ -33,7 +33,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.23.7"
go-version: "1.24.6"
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
@@ -41,7 +41,7 @@ jobs:
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
version: "116"
- name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli
+7 -4
View File
@@ -4,7 +4,7 @@ on:
jobs:
publish:
runs-on: arc-ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -17,10 +17,13 @@ jobs:
- name: Setup yarn
run: npm install -g yarn
- name: Install Rust stable
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
@@ -29,9 +32,9 @@ jobs:
run: cargo install wasm-opt
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "1.23.7"
go-version: "1.24.6"
- name: Install dependencies
run: yarn
+3 -30
View File
@@ -3,11 +3,6 @@ name: Build and upload Node Status agent container to harbor.nymte.ch
on:
workflow_dispatch:
inputs:
gateway_probe_git_ref:
type: string
default: nym-vpn-core-v1.4.0
required: true
description: Which gateway probe git ref to build the image with
release_image:
description: 'Tag image as a release'
required: true
@@ -43,16 +38,6 @@ jobs:
VERSION=$(yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml)
echo "result=$VERSION" >> $GITHUB_OUTPUT
- name: cleanup-gateway-probe-ref
id: cleanup_gateway_probe_ref
run: |
GATEWAY_PROBE_GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }}
GIT_REF_SLUG="${GATEWAY_PROBE_GIT_REF//\//-}"
echo "git_ref=${GIT_REF_SLUG}" >> $GITHUB_OUTPUT
- name: Set GIT_TAG variable
run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
- name: Initialize RELEASE_TAG
run: echo "RELEASE_TAG=" >> $GITHUB_ENV
@@ -61,24 +46,12 @@ jobs:
run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV
- name: Set IMAGE_NAME_AND_TAGS variable
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
- name: New env vars
run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
# - name: Remove existing tag if exists
# run: |
# if git rev-parse $${{ env.GIT_TAG }} >/dev/null 2>&1; then
# git push --delete origin $${{ env.GIT_TAG }}
# git tag -d $${{ env.GIT_TAG }}
# fi
# - name: Create tag
# run: |
# git tag -a $${{ env.GIT_TAG }} -m "Version ${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}"
# git push origin $${{ env.GIT_TAG }}
run: echo "RELEASE_TAG='$RELEASE_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
- name: BuildAndPushImageOnHarbor
run: |
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+40
View File
@@ -4,6 +4,46 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.19-kase] (2025-10-30)
- update ns agent workflow ([#6154])
- Cherry pick - request #6143 from nymtech/bugfix/mix-tx-closed-v2 ([#6153])
- bugfix: nym-credential-proxy query params parsing regression ([#6121])
- bugfix: revert some dep updates introduced in #6043 ([#6120])
- Skip ipv6 metadata endpoint request ([#6118])
- update to no longer use 1mb files ([#6117])
- chore: restore pending dkg contract state migration ([#6116])
- Revert "Propagate cancel token to mixnet client" ([#6115])
- Update dirs to 6.0 ([#6109])
- Propagate cancel token to mixnet client ([#6105])
- bugfix: retrieve and update ticketbook in the same query ([#6101])
- bugfix: include network name in the default gateway probe config path ([#6100])
- Bugfix/incompatibility fixes ([#6099])
- [DOCs/operators] QUIC deployment script & docs ([#6098])
- bugfix: testnet manager 02sql migration ([#6096])
- feat: move gateway probe to monorepo (and update to rust edition 2024) ([#6094])
- bugfix: use custom topology provider for list of init gateways ([#6092])
- Max/fix wasm client + build commands ([#6043])
[#6154]: https://github.com/nymtech/nym/pull/6154
[#6153]: https://github.com/nymtech/nym/pull/6153
[#6121]: https://github.com/nymtech/nym/pull/6121
[#6120]: https://github.com/nymtech/nym/pull/6120
[#6118]: https://github.com/nymtech/nym/pull/6118
[#6117]: https://github.com/nymtech/nym/pull/6117
[#6116]: https://github.com/nymtech/nym/pull/6116
[#6115]: https://github.com/nymtech/nym/pull/6115
[#6109]: https://github.com/nymtech/nym/pull/6109
[#6105]: https://github.com/nymtech/nym/pull/6105
[#6101]: https://github.com/nymtech/nym/pull/6101
[#6100]: https://github.com/nymtech/nym/pull/6100
[#6099]: https://github.com/nymtech/nym/pull/6099
[#6098]: https://github.com/nymtech/nym/pull/6098
[#6096]: https://github.com/nymtech/nym/pull/6096
[#6094]: https://github.com/nymtech/nym/pull/6094
[#6092]: https://github.com/nymtech/nym/pull/6092
[#6043]: https://github.com/nymtech/nym/pull/6043
## [2025.18-jarlsberg] (2025-10-14)
- ns-api: add descriptions to dVPN gateway responses ([#6102])
Generated
+306 -30
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "accessory"
@@ -133,9 +133,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "ammonia"
version = "4.1.1"
version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f"
checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6"
dependencies = [
"cssparser",
"html5ever",
@@ -929,7 +929,7 @@ dependencies = [
[[package]]
name = "bls12_381"
version = "0.8.0"
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd"
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp%2Fexperimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd"
dependencies = [
"digest 0.10.7",
"ff",
@@ -2135,6 +2135,37 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.106",
]
[[package]]
name = "derive_more"
version = "1.0.0"
@@ -2215,23 +2246,23 @@ dependencies = [
[[package]]
name = "dirs"
version = "5.0.1"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -2548,7 +2579,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.4.0-rc.0"
version = "1.4.1"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -4447,7 +4478,7 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.4.0-rc.0"
version = "1.4.1"
dependencies = [
"async-trait",
"futures",
@@ -4457,6 +4488,7 @@ dependencies = [
"nym-ordered-buffer",
"nym-service-providers-common",
"nym-socks5-requests",
"nym-validator-client",
"rand 0.8.5",
"serde",
"serde-wasm-bindgen 0.6.5",
@@ -4650,6 +4682,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "no-std-net"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
[[package]]
name = "nom"
version = "7.1.3"
@@ -4787,7 +4825,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.67"
version = "1.1.68"
dependencies = [
"anyhow",
"async-trait",
@@ -5001,7 +5039,7 @@ dependencies = [
"tracing-subscriber",
"tracing-tree",
"utoipa",
"vergen",
"vergen 8.3.1",
]
[[package]]
@@ -5013,7 +5051,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.64"
version = "1.1.65"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -5096,7 +5134,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.64"
version = "1.1.65"
dependencies = [
"bs58",
"clap",
@@ -5181,6 +5219,7 @@ dependencies = [
"tokio",
"tokio-stream",
"tokio-tungstenite",
"tokio_with_wasm",
"tracing",
"tungstenite 0.20.1",
"url",
@@ -5245,25 +5284,30 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.4.0-rc.0"
version = "1.4.1"
dependencies = [
"anyhow",
"futures",
"gloo-timers",
"js-sys",
"nym-bin-common",
"nym-gateway-requests",
"nym-node-tester-utils",
"nym-node-tester-wasm",
"once_cell",
"rand 0.8.5",
"serde",
"serde-wasm-bindgen 0.6.5",
"serde_json",
"thiserror 2.0.12",
"tokio_with_wasm",
"tsify",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test",
"wasm-client-core",
"wasm-utils",
"web-sys",
]
[[package]]
@@ -5288,6 +5332,14 @@ dependencies = [
"nym-multisig-contract-common",
]
[[package]]
name = "nym-common"
version = "1.18.0"
dependencies = [
"tracing",
"tracing-test",
]
[[package]]
name = "nym-compact-ecash"
version = "0.1.0"
@@ -5326,6 +5378,24 @@ dependencies = [
"url",
]
[[package]]
name = "nym-connection-monitor"
version = "1.18.0"
dependencies = [
"bincode",
"bytes",
"futures",
"nym-common",
"nym-config",
"nym-ip-packet-requests",
"nym-sdk",
"pnet_packet",
"thiserror 2.0.12",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "nym-contracts-common"
version = "0.5.0"
@@ -5340,7 +5410,7 @@ dependencies = [
"serde_json",
"thiserror 2.0.12",
"utoipa",
"vergen",
"vergen 8.3.1",
]
[[package]]
@@ -5373,7 +5443,7 @@ dependencies = [
[[package]]
name = "nym-credential-proxy"
version = "0.3.0"
version = "0.3.0-test"
dependencies = [
"anyhow",
"axum",
@@ -5393,8 +5463,11 @@ dependencies = [
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-signer-check",
"nym-http-api-client",
"nym-http-api-common",
"nym-network-defaults",
"nym-pemstore",
"nym-upgrade-mode-check",
"nym-validator-client",
"rand 0.8.5",
"reqwest 0.12.22",
@@ -5465,10 +5538,12 @@ dependencies = [
"nym-http-api-client",
"nym-http-api-common",
"nym-serde-helpers",
"nym-upgrade-mode-check",
"reqwest 0.12.22",
"schemars 0.8.22",
"serde",
"serde_json",
"serde_with",
"time",
"tsify",
"utoipa",
@@ -5591,6 +5666,7 @@ dependencies = [
"aead",
"aes",
"aes-gcm-siv",
"anyhow",
"base64 0.22.1",
"blake3",
"bs58",
@@ -5604,10 +5680,12 @@ dependencies = [
"jwt-simple",
"nym-pemstore",
"nym-sphinx-types",
"nym-test-utils",
"rand 0.8.5",
"rand_chacha 0.3.1",
"serde",
"serde_bytes",
"serde_json",
"sha2 0.10.9",
"subtle-encoding",
"thiserror 2.0.12",
@@ -5625,7 +5703,6 @@ dependencies = [
"criterion",
"ff",
"group",
"lazy_static",
"nym-contracts-common",
"nym-pemstore",
"rand 0.8.5",
@@ -5814,6 +5891,51 @@ dependencies = [
"zeroize",
]
[[package]]
name = "nym-gateway-probe"
version = "1.18.0"
dependencies = [
"anyhow",
"base64 0.22.1",
"bincode",
"bs58",
"bytes",
"clap",
"futures",
"hex",
"nym-authenticator-client",
"nym-authenticator-requests",
"nym-bandwidth-controller",
"nym-bin-common",
"nym-client-core",
"nym-config",
"nym-connection-monitor",
"nym-credential-utils",
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-http-api-client",
"nym-http-api-client-macro",
"nym-ip-packet-client",
"nym-ip-packet-requests",
"nym-node-status-client",
"nym-sdk",
"nym-topology",
"nym-validator-client",
"pnet_packet",
"rand 0.8.5",
"serde",
"serde_json",
"thiserror 2.0.12",
"tokio",
"tokio-util",
"tracing",
"tracing-subscriber",
"url",
"vergen-gitcl",
"x25519-dalek",
]
[[package]]
name = "nym-gateway-requests"
version = "0.1.0"
@@ -5936,6 +6058,7 @@ dependencies = [
"thiserror 2.0.12",
"tokio",
"tracing",
"tracing-subscriber",
"url",
"wasmtimer",
]
@@ -6240,7 +6363,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.65"
version = "1.1.66"
dependencies = [
"addr",
"anyhow",
@@ -6290,7 +6413,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.19.0"
version = "1.20.0"
dependencies = [
"anyhow",
"arc-swap",
@@ -6432,7 +6555,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "4.0.5"
version = "4.0.11-rc1"
dependencies = [
"ammonia",
"anyhow",
@@ -6528,7 +6651,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.3.0-rc.0"
version = "1.3.1"
dependencies = [
"futures",
"js-sys",
@@ -6671,6 +6794,7 @@ dependencies = [
name = "nym-registration-client"
version = "0.1.0"
dependencies = [
"futures",
"nym-authenticator-client",
"nym-bandwidth-controller",
"nym-credential-storage",
@@ -6683,6 +6807,7 @@ dependencies = [
"tokio",
"tokio-util",
"tracing",
"typed-builder",
"url",
]
@@ -6815,7 +6940,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.64"
version = "1.1.65"
dependencies = [
"bs58",
"clap",
@@ -6998,6 +7123,7 @@ dependencies = [
"serde",
"thiserror 2.0.12",
"utoipa",
"wasmtimer",
]
[[package]]
@@ -7261,12 +7387,14 @@ dependencies = [
"jwt-simple",
"nym-crypto",
"nym-http-api-client",
"nym-test-utils",
"reqwest 0.12.22",
"serde",
"serde_json",
"thiserror 2.0.12",
"time",
"tracing",
"utoipa",
]
[[package]]
@@ -7553,7 +7681,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.29"
version = "0.1.30"
dependencies = [
"anyhow",
"bytes",
@@ -7707,9 +7835,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.109"
version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
dependencies = [
"cc",
"libc",
@@ -8159,6 +8287,48 @@ dependencies = [
"plotters-backend",
]
[[package]]
name = "pnet_base"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7"
dependencies = [
"no-std-net",
]
[[package]]
name = "pnet_macros"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn 2.0.106",
]
[[package]]
name = "pnet_macros_support"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab"
dependencies = [
"pnet_base",
]
[[package]]
name = "pnet_packet"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f"
dependencies = [
"glob",
"pnet_base",
"pnet_macros",
"pnet_macros_support",
]
[[package]]
name = "polling"
version = "2.8.0"
@@ -8614,13 +8784,13 @@ dependencies = [
[[package]]
name = "redox_users"
version = "0.4.6"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror 1.0.69",
"thiserror 2.0.12",
]
[[package]]
@@ -10753,6 +10923,30 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio_with_wasm"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dfba9b946459940fb564dcf576631074cdfb0bfe4c962acd4c31f0dca7897e6"
dependencies = [
"js-sys",
"tokio",
"tokio_with_wasm_proc",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "tokio_with_wasm_proc"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e04c1865c281139e5ccf633cb9f76ffdaabeebfe53b703984cf82878e2aabb"
dependencies = [
"quote",
"syn 2.0.106",
]
[[package]]
name = "toml"
version = "0.5.11"
@@ -11035,6 +11229,27 @@ dependencies = [
"tracing-log 0.2.0",
]
[[package]]
name = "tracing-test"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68"
dependencies = [
"tracing-core",
"tracing-subscriber",
"tracing-test-macro",
]
[[package]]
name = "tracing-test-macro"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568"
dependencies = [
"quote",
"syn 2.0.106",
]
[[package]]
name = "tracing-tree"
version = "0.2.5"
@@ -11188,6 +11403,26 @@ dependencies = [
"utf-8",
]
[[package]]
name = "typed-builder"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d0dd654273fc253fde1df4172c31fb6615cf8b041d3a4008a028ef8b1119e66"
dependencies = [
"typed-builder-macro",
]
[[package]]
name = "typed-builder-macro"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c26257f448222014296978b2c8456e2cad4de308c35bdb1e383acd569ef5b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "typenum"
version = "1.18.0"
@@ -11587,6 +11822,47 @@ dependencies = [
"time",
]
[[package]]
name = "vergen"
version = "9.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777"
dependencies = [
"anyhow",
"cargo_metadata 0.19.2",
"derive_builder",
"regex",
"rustc_version 0.4.1",
"rustversion",
"time",
"vergen-lib",
]
[[package]]
name = "vergen-gitcl"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9dfc1de6eb2e08a4ddf152f1b179529638bedc0ea95e6d667c014506377aefe"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
"time",
"vergen 9.0.6",
"vergen-lib",
]
[[package]]
name = "vergen-lib"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
]
[[package]]
name = "version_check"
version = "0.9.5"
+26 -22
View File
@@ -31,6 +31,7 @@ members = [
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
"common/commands",
"common/nym-common",
"common/config",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
@@ -58,7 +59,8 @@ members = [
"common/gateway-requests",
"common/gateway-stats-storage",
"common/gateway-storage",
"common/http-api-client", "common/http-api-client-macro",
"common/http-api-client",
"common/http-api-client-macro",
"common/http-api-common",
"common/inclusion-probability",
"common/ip-packet-requests",
@@ -66,7 +68,9 @@ members = [
"common/mixnode-common",
"common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue", "common/nym-cache",
"common/nonexhaustive-delayqueue",
"common/nym-cache",
"common/nym-connection-monitor",
"common/nym-id",
"common/nym-metrics",
"common/nym_offline_compact_ecash",
@@ -98,7 +102,8 @@ members = [
"common/ticketbooks-merkle",
"common/topology",
"common/tun",
"common/types", "common/upgrade-mode-check",
"common/types",
"common/upgrade-mode-check",
"common/verloc",
"common/wasm/client-core",
"common/wasm/storage",
@@ -127,7 +132,7 @@ members = [
"nym-node-status-api/nym-node-status-client",
"nym-node/nym-node-metrics",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-outfox",
"nym-registration-client",
"nym-signers-monitor",
"nym-statistics-api",
@@ -145,7 +150,7 @@ members = [
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/mixnet-connectivity-check",
# "tools/internal/sdk-version-bump",
# "tools/internal/sdk-version-bump",
"tools/internal/ssl-inject",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
@@ -160,6 +165,7 @@ members = [
"wasm/mix-fetch",
"wasm/node-tester",
"wasm/zknym-lib",
"nym-gateway-probe"
]
default-members = [
@@ -178,16 +184,16 @@ default-members = [
"tools/nymvisor",
]
exclude = ["explorer", "contracts", "nym-wallet", "cpu-cycles"]
exclude = ["contracts", "nym-wallet", "cpu-cycles"]
[workspace.package]
authors = ["Nym Technologies SA"]
repository = "https://github.com/nymtech/nym"
homepage = "https://nymtech.net"
documentation = "https://nymtech.net"
edition = "2021"
edition = "2024"
license = "Apache-2.0"
rust-version = "1.81"
rust-version = "1.85"
readme = "README.md"
[workspace.dependencies]
@@ -209,7 +215,6 @@ base64 = "0.22.1"
base85rs = "0.1.3"
bincode = "1.3.3"
bip39 = { version = "2.0.0", features = ["zeroize"] }
bit-vec = "0.7.0" # can we unify those?
bitvec = "1.0.0"
blake3 = "1.7.0"
bloomfilter = "3.0.1"
@@ -237,13 +242,11 @@ criterion = "0.5"
csv = "1.3.1"
ctr = "0.9.1"
cupid = "0.6.1"
curve25519-dalek = "4.1"
dashmap = "5.5.3"
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
digest = "0.10.7"
dirs = "5.0"
doc-comment = "0.3"
dirs = "6.0"
dotenvy = "0.15.6"
dyn-clone = "1.0.19"
ecdsa = "0.16"
@@ -259,11 +262,8 @@ futures = "0.3.31"
futures-util = "0.3"
generic-array = "0.14.7"
getrandom = "0.2.10"
getset = "0.1.5"
handlebars = "3.5.5"
headers = "0.4.0"
hex = "0.4.3"
hex-literal = "0.3.3"
hickory-resolver = "0.25"
hkdf = "0.12.3"
hmac = "0.12.1"
@@ -287,12 +287,10 @@ lazy_static = "1.5.0"
ledger-transport = "0.10.0"
ledger-transport-hid = "0.10.0"
log = "0.4"
maxminddb = "0.23.0"
mime = "0.3.17"
moka = { version = "0.12", features = ["future"] }
nix = "0.27.1"
notify = "5.1.0"
okapi = "0.7.0"
once_cell = "1.21.3"
opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0"
@@ -300,7 +298,7 @@ parking_lot = "0.12.3"
pem = "0.8"
petgraph = "0.6.5"
pin-project = "1.1"
pin-project-lite = "0.2.16"
pnet_packet = "0.35.0"
publicsuffix = "2.3.0"
proc_pidinfo = "0.1.3"
quote = "1"
@@ -308,13 +306,10 @@ rand = "0.8.5"
rand_chacha = "0.3"
rand_core = "0.6.3"
rand_distr = "0.4"
rand_pcg = "0.3.1"
rand_seeder = "0.2.3"
rayon = "1.5.1"
regex = "1.10.6"
reqwest = { version = "0.12.15", default-features = false }
rs_merkle = "1.5.0"
safer-ffi = "0.1.13"
schemars = "0.8.22"
semver = "1.0.26"
serde = "1.0.219"
@@ -358,8 +353,10 @@ tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19"
tracing-tree = "0.2.2"
tracing-indicatif = "0.3.9"
tracing-test = "0.2.5"
ts-rs = "10.1.0"
tungstenite = { version = "0.20.1", default-features = false }
typed-builder = "0.23.0"
uniffi = "0.29.2"
uniffi_build = "0.29.0"
url = "2.5"
@@ -368,6 +365,7 @@ utoipa-swagger-ui = "8.1"
utoipauto = "0.2"
uuid = "*"
vergen = { version = "=8.3.1", default-features = false }
vergen-gitcl = { version = "1.0.8", default-features = false }
walkdir = "2"
x25519-dalek = "2.0.0"
zeroize = "1.7.0"
@@ -407,18 +405,19 @@ prost = { version = "0.13", default-features = false }
# wasm-related dependencies
gloo-utils = "0.2.0"
gloo-net = "0.6.0"
gloo-timers = "0.3.0"
indexed_db_futures = "0.6.4"
js-sys = "0.3.76"
serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5"
tokio_with_wasm = { version = "0.8.7" }
wasm-bindgen = "0.2.99"
wasm-bindgen-futures = "0.4.49"
wasm-bindgen-test = "0.3.49"
wasmtimer = "0.4.1"
web-sys = "0.3.76"
# for local development:
#[patch.crates-io]
#sphinx-packet = { path = "../sphinx" }
@@ -454,6 +453,11 @@ opt-level = 'z'
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] }
[workspace.lints.clippy]
suspicious = "deny"
complexity = "deny"
perf = "deny"
style = "deny"
unwrap_used = "deny"
expect_used = "deny"
todo = "deny"
+1 -1
View File
@@ -109,7 +109,7 @@ sdk-wasm-build:
$(MAKE) -C wasm/node-tester
$(MAKE) -C wasm/mix-fetch
$(MAKE) -C wasm/zknym-lib
#$(MAKE) -C wasm/full-nym-wasm
# $(MAKE) -C wasm/full-nym-wasm
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
sdk-typescript-build:
+2 -2
View File
@@ -1,10 +1,10 @@
[package]
name = "nym-client"
version = "1.1.64"
version = "1.1.65"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
rust-version = "1.70"
rust-version = "1.85"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -60,6 +60,7 @@ impl SocketClient {
let ClientInput {
connection_command_sender,
input_sender,
..
} = client_input;
let ClientOutput {
+2 -2
View File
@@ -1,10 +1,10 @@
[package]
name = "nym-socks5-client"
version = "1.1.64"
version = "1.1.65"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
rust-version = "1.70"
rust-version = "1.85"
license.workspace = true
[dependencies]
+5 -5
View File
@@ -1,8 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use futures::channel::mpsc;
use futures::StreamExt;
use futures::channel::mpsc;
use notify::event::{DataChange, MetadataKind, ModifyKind};
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::collections::HashMap;
@@ -96,10 +96,10 @@ impl AsyncFileWatcher {
// when testing I was consistently getting two `Modify(Data(Any))` events in quick succession
// (probably to modify content and metadata).
// we really only want to propagate one of them
if let Some(previous) = self.last_received.get(&event.kind) {
if now.duration_since(*previous) < self.tick_duration {
return false;
}
if let Some(previous) = self.last_received.get(&event.kind)
&& now.duration_since(*previous) < self.tick_duration
{
return false;
}
let Some(filters) = &self.filters else {
@@ -5,9 +5,10 @@ use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use crate::{
AuthenticatorVersion, Error,
latest::registration::IpPair,
traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, Versionable},
v2, v3, v4, v5, AuthenticatorVersion, Error,
v2, v3, v4, v5,
};
// This is very redundant with AuthenticatorRequest and I reckon they could be smooshed.
+1 -1
View File
@@ -9,7 +9,7 @@ use nym_crypto::asymmetric::x25519::PrivateKey;
use nym_wireguard_types::PeerPublicKey;
use crate::latest::registration::IpPair;
use crate::{v1, v2, v3, v4, v5, AuthenticatorVersion, Error};
use crate::{AuthenticatorVersion, Error, v1, v2, v3, v4, v5};
pub trait Versionable {
fn version(&self) -> AuthenticatorVersion;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use base64::{engine::general_purpose, Engine};
use base64::{Engine, engine::general_purpose};
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use base64::{engine::general_purpose, Engine};
use base64::{Engine, engine::general_purpose};
use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use base64::{engine::general_purpose, Engine};
use base64::{Engine, engine::general_purpose};
use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use base64::{engine::general_purpose, Engine};
use base64::{Engine, engine::general_purpose};
use nym_credentials_interface::CredentialSpendingData;
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
use nym_wireguard_types::PeerPublicKey;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use base64::{engine::general_purpose, Engine};
use base64::{Engine, engine::general_purpose};
use nym_credentials_interface::CredentialSpendingData;
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
use nym_wireguard_types::PeerPublicKey;
+5 -1
View File
@@ -129,7 +129,11 @@ impl From<semver::Version> for AuthenticatorVersion {
// if provided version is higher (or equal) to release version of V5,
// we return the latest (i.e. v5)
debug_assert_eq!(Self::V5, Self::LATEST, "a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait");
debug_assert_eq!(
Self::V5,
Self::LATEST,
"a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait"
);
Self::LATEST
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
use clap::Args;
use clap::builder::Command;
use clap::clap_derive::ValueEnum;
use clap::Args;
use clap_complete::generator::generate;
use clap_complete::Shell as ClapShell;
use clap_complete::generator::generate;
use std::io;
pub fn fig_generate(command: &mut Command, name: &str) {
+7 -4
View File
@@ -3,7 +3,7 @@ name = "nym-client-core"
version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.76"
rust-version = "1.85"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -36,7 +36,7 @@ nym-bandwidth-controller = { path = "../bandwidth-controller" }
nym-crypto = { path = "../crypto" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
nym-gateway-requests = { path = "../gateway-requests" }
nym-http-api-client = { path = "../http-api-client" }
nym-http-api-client = { path = "../http-api-client", features = ["network-defaults"] }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" }
nym-statistics-common = { path = "../statistics" }
@@ -69,7 +69,6 @@ workspace = true
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper-util]
workspace = true
features = ["tokio"]
###
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
workspace = true
@@ -103,7 +102,7 @@ workspace = true
features = ["tokio"]
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.3.0"
workspace = true
features = ["futures"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
@@ -114,6 +113,10 @@ features = ["websocket"]
workspace = true
features = ["wasm-bindgen"]
[target."cfg(target_arch = \"wasm32\")".dependencies.tokio_with_wasm]
workspace = true
features = ["full"]
[dev-dependencies]
tempfile = { workspace = true }
@@ -707,13 +707,10 @@ pub struct DebugConfig {
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
/// Defines all configuration options related to stats reporting.
pub stats_reporting: StatsReporting,
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMe,
/// Defines all configuration options related to the remember me flag.
pub remember_me: RememberMe,
}
@@ -543,10 +543,8 @@ pub struct DebugConfigV6 {
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV6,
/// Defines all configuration options related to stats reporting.
pub stats_reporting: StatsReportingV6,
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMeV6,
@@ -31,7 +31,6 @@ impl StorageManager {
}
})?;
}
let opts = sqlx::sqlite::SqliteConnectOptions::new()
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
@@ -114,13 +114,12 @@ where
})?;
hardcoded_topology.entry_capable_nodes().cloned().collect()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init(
&mut rng,
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
None,
)
.await?
};
@@ -173,13 +173,12 @@ where
})?;
hardcoded_topology.entry_capable_nodes().cloned().collect()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init(
&mut rng,
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
None,
)
.await?
};
+220 -36
View File
@@ -7,11 +7,12 @@ use super::statistics_control::StatisticsControl;
use crate::client::base_client::storage::helpers::store_client_keys;
use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::event_control::EventControl;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ClientKeys;
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController, MixTrafficEvent};
use crate::client::real_messages_control;
use crate::client::real_messages_control::RealMessagesController;
use crate::client::received_buffer::{
@@ -66,9 +67,16 @@ use std::path::Path;
use std::sync::Arc;
use time::OffsetDateTime;
use tokio::sync::mpsc::Sender;
use tracing::*;
use url::Url;
#[cfg(target_arch = "wasm32")]
#[cfg(debug_assertions)]
use wasm_utils::console_log;
/// Default number of retries for Nym API requests when using network details with domain fronting.
/// This allows the client to try alternative URLs if the primary endpoint is unavailable.
const DEFAULT_NYM_API_RETRIES: usize = 3;
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
@@ -79,10 +87,28 @@ pub mod non_wasm_helpers;
pub mod helpers;
pub mod storage;
#[derive(Clone, Copy, Debug)]
pub enum MixnetClientEvent {
Traffic(MixTrafficEvent),
}
pub type EventReceiver = mpsc::UnboundedReceiver<MixnetClientEvent>;
#[derive(Clone)]
pub struct EventSender(pub mpsc::UnboundedSender<MixnetClientEvent>);
impl EventSender {
pub fn send(&self, event: MixnetClientEvent) {
if let Err(err) = self.0.unbounded_send(event) {
tracing::warn!("Failed to send error event. The caller event reader was closed: {err}");
}
}
}
#[derive(Clone)]
pub struct ClientInput {
pub connection_command_sender: ConnectionCommandSender,
pub input_sender: InputMessageSender,
pub client_request_sender: ClientRequestSender,
}
impl ClientInput {
@@ -190,10 +216,14 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
client_store: S,
dkg_query_client: Option<C>,
// Optional API URLs for domain fronting support
nym_api_urls: Option<Vec<nym_network_defaults::ApiUrl>>,
wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
shutdown: Option<ShutdownTracker>,
event_tx: Option<EventSender>,
user_agent: Option<UserAgent>,
setup_method: GatewaySetup,
@@ -218,10 +248,12 @@ where
config: base_config,
client_store,
dkg_query_client,
nym_api_urls: None,
wait_for_gateway: false,
custom_topology_provider: None,
custom_gateway_transceiver: None,
shutdown: None,
event_tx: None,
user_agent: None,
setup_method: GatewaySetup::MustLoad { gateway_id: None },
#[cfg(unix)]
@@ -239,6 +271,16 @@ where
self
}
/// Set Nym API URLs for domain fronting support.
///
/// When provided, the client will use these API URLs (which include front_hosts)
/// to construct HTTP clients with domain fronting enabled.
#[must_use]
pub fn with_nym_api_urls(mut self, nym_api_urls: Vec<nym_network_defaults::ApiUrl>) -> Self {
self.nym_api_urls = Some(nym_api_urls);
self
}
#[must_use]
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
self.config.debug.forget_me = *forget_me;
@@ -284,6 +326,12 @@ where
self
}
#[must_use]
pub fn with_event_tx(mut self, event_tx: EventSender) -> Self {
self.event_tx = Some(event_tx);
self
}
#[must_use]
pub fn with_user_agent(mut self, user_agent: UserAgent) -> Self {
self.user_agent = Some(user_agent);
@@ -314,6 +362,18 @@ where
details.client_address()
}
fn start_event_control(
parent_event_tx: Option<EventSender>,
children_event_rx: EventReceiver,
shutdown_tracker: &ShutdownTracker,
) {
let event_control = EventControl::new(parent_event_tx, children_event_rx);
shutdown_tracker.try_spawn_named_with_shutdown(
async move { event_control.run().await },
"EventControl",
);
}
// future constantly pumping loop cover traffic at some specified average rate
// the pumped traffic goes to the MixTrafficController
fn start_cover_traffic_stream(
@@ -325,7 +385,7 @@ where
stats_tx: ClientStatsSender,
shutdown_tracker: &ShutdownTracker,
) {
info!("Starting loop cover traffic stream...");
tracing::info!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new(
ack_key,
@@ -357,7 +417,7 @@ where
stats_tx: ClientStatsSender,
shutdown_tracker: &ShutdownTracker,
) {
info!("Starting real traffic stream...");
tracing::info!("Starting real traffic stream...");
let real_messages_controller = RealMessagesController::new(
controller_config,
@@ -442,7 +502,7 @@ where
metrics_reporter: ClientStatsSender,
shutdown_tracker: &ShutdownTracker,
) {
info!("Starting received messages buffer controller...");
tracing::info!("Starting received messages buffer controller...");
let controller = ReceivedMessagesBufferController::<SphinxMessageReceiver>::new(
local_encryption_keypair,
query_receiver,
@@ -553,7 +613,7 @@ where
details_store
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
.await.map_err(|err| {
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
tracing::error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
})?
}
@@ -650,7 +710,7 @@ where
if topology_config.disable_refreshing {
// if we're not spawning the refresher, don't cause shutdown immediately
info!("The background topology refresher is not going to be started");
tracing::info!("The background topology refresher is not going to be started");
}
let mut topology_refresher = TopologyRefresher::new(
@@ -660,7 +720,7 @@ where
);
// before returning, block entire runtime to refresh the current network view so that any
// components depending on topology would see a non-empty view
info!("Obtaining initial network topology");
tracing::info!("Obtaining initial network topology");
topology_refresher.try_refresh().await;
if let Err(err) = topology_refresher.ensure_topology_is_routable().await {
@@ -686,13 +746,13 @@ where
.wait_for_gateway(local_gateway, waiting_timeout)
.await
{
error!(
tracing::error!(
"the gateway did not come back online within the specified timeout: {err}"
);
return Err(err.into());
}
} else {
error!("the gateway we're supposedly connected to does not exist. We'll not be able to send any packets to ourselves: {err}");
tracing::error!("the gateway we're supposedly connected to does not exist. We'll not be able to send any packets to ourselves: {err}");
return Err(err.into());
}
}
@@ -700,7 +760,7 @@ where
if !topology_config.disable_refreshing {
// don't spawn the refresher if we don't want to be refreshing the topology.
// only use the initial values obtained
info!("Starting topology refresher...");
tracing::info!("Starting topology refresher...");
shutdown_tracker.try_spawn_named_with_shutdown(
async move { topology_refresher.run().await },
"TopologyRefresher",
@@ -717,7 +777,7 @@ where
input_sender: Sender<InputMessage>,
shutdown_tracker: &ShutdownTracker,
) -> ClientStatsSender {
info!("Starting statistics control...");
tracing::info!("Starting statistics control...");
StatisticsControl::create_and_start(
config.debug.stats_reporting,
user_agent
@@ -732,10 +792,17 @@ where
fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown_tracker: &ShutdownTracker,
event_tx: EventSender,
) -> (BatchMixMessageSender, ClientRequestSender) {
info!("Starting mix traffic controller...");
let (mut mix_traffic_controller, mix_tx, client_tx) =
MixTrafficController::new(gateway_transceiver, shutdown_tracker.clone_shutdown_token());
tracing::info!("Starting mix traffic controller...");
let mut mix_traffic_controller = MixTrafficController::new(
gateway_transceiver,
shutdown_tracker.clone_shutdown_token(),
event_tx,
);
let mix_tx = mix_traffic_controller.mix_tx();
let client_tx = mix_traffic_controller.client_tx();
shutdown_tracker.try_spawn_named(
async move { mix_traffic_controller.run().await },
@@ -799,7 +866,7 @@ where
{
// if client keys do not exist already, create and persist them
if key_store.load_keys().await.is_err() {
info!("could not find valid client keys - a new set will be generated");
tracing::info!("could not find valid client keys - a new set will be generated");
let mut rng = OsRng;
let keys = if let Some(derivation_material) = derivation_material {
ClientKeys::from_master_key(&mut rng, &derivation_material)
@@ -814,21 +881,67 @@ where
}
fn construct_nym_api_client(
nym_api_urls: Option<&Vec<nym_network_defaults::ApiUrl>>,
config: &Config,
user_agent: Option<UserAgent>,
) -> Result<nym_http_api_client::Client, ClientCoreError> {
tracing::debug!(
"construct_nym_api_client called with nym_api_urls: {}",
nym_api_urls.is_some()
);
// If API URLs are provided, use new_with_fronted_urls() which handles domain fronting
if let Some(nym_api_urls) = nym_api_urls {
if nym_api_urls.is_empty() {
tracing::warn!("Provided nym_api_urls is empty, falling back to config endpoints");
} else {
tracing::info!(
"Building nym-api client from provided URLs (with domain fronting support): {} URLs",
nym_api_urls.len()
);
let mut builder =
nym_http_api_client::ClientBuilder::new_with_fronted_urls(nym_api_urls.clone())
.map_err(ClientCoreError::from)?
.with_retries(DEFAULT_NYM_API_RETRIES);
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
}
return builder.build().map_err(ClientCoreError::from);
}
}
// Fallback to basic client for backwards compatibility
tracing::debug!("Building basic nym-api HTTP client from config endpoints");
let mut nym_api_urls = config.get_nym_api_endpoints();
if nym_api_urls.is_empty() {
tracing::warn!("No API endpoints configured in config, this may cause issues");
}
nym_api_urls.shuffle(&mut thread_rng());
let mut builder = nym_http_api_client::Client::builder(nym_api_urls[0].clone())
.map_err(ClientCoreError::from)?;
// Convert config URLs to ApiUrl format for consistency
let api_urls: Vec<nym_network_defaults::ApiUrl> = nym_api_urls
.into_iter()
.map(|url| nym_network_defaults::ApiUrl {
url: url.to_string(),
front_hosts: None,
})
.collect();
tracing::debug!("Using {} config API endpoints", api_urls.len());
let mut builder = nym_http_api_client::ClientBuilder::new_with_fronted_urls(api_urls)
.map_err(ClientCoreError::from)?
.with_retries(DEFAULT_NYM_API_RETRIES)
.with_bincode();
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
}
builder = builder.with_bincode();
builder.build().map_err(ClientCoreError::from)
}
@@ -846,7 +959,12 @@ where
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
info!("Starting nym client");
tracing::info!("Starting nym client");
#[cfg(debug_assertions)]
#[cfg(target_arch = "wasm32")]
{
console_log!("Starting base Nym Client");
}
// derive (or load) client keys and gateway configuration
let init_res = Self::initialise_keys_and_gateway(
@@ -875,6 +993,9 @@ where
// channels responsible for controlling real messages
let (input_sender, input_receiver) = tokio::sync::mpsc::channel::<InputMessage>(1);
// channels responsible for event management
let (event_sender, event_receiver) = mpsc::unbounded();
// channels responsible for controlling ack messages
let (ack_sender, ack_receiver) = mpsc::unbounded();
let shared_topology_accessor =
@@ -883,10 +1004,12 @@ where
// Create a shutdown tracker for this client - either as a child of provided tracker
// or get one from the registry
let shutdown_tracker = match self.shutdown {
Some(parent_tracker) => parent_tracker.child_tracker(),
None => nym_task::get_sdk_shutdown_tracker()?,
Some(parent_tracker) => parent_tracker.clone(),
None => nym_task::create_sdk_shutdown_tracker()?,
};
Self::start_event_control(self.event_tx, event_receiver, &shutdown_tracker);
// channels responsible for dealing with reply-related fun
let (reply_controller_sender, reply_controller_receiver) =
reply_controller::requests::new_control_channels();
@@ -902,7 +1025,11 @@ where
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone())?;
let nym_api_client = Self::construct_nym_api_client(
self.nym_api_urls.as_ref(),
&self.config,
self.user_agent.clone(),
)?;
let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?;
let topology_provider = Self::setup_topology_provider(
@@ -917,7 +1044,7 @@ where
self.user_agent.clone(),
generate_client_stats_id(*self_address.identity()),
input_sender.clone(),
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
);
// needs to be started as the first thing to block if required waiting for the gateway
@@ -927,7 +1054,7 @@ where
shared_topology_accessor.clone(),
self_address.gateway(),
self.wait_for_gateway,
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
)
.await?;
@@ -947,7 +1074,7 @@ where
stats_reporter.clone(),
#[cfg(unix)]
self.connection_fd_callback,
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
)
.await?;
let gateway_ws_fd = gateway_transceiver.ws_fd();
@@ -955,7 +1082,7 @@ where
let reply_storage = Self::setup_persistent_reply_storage(
reply_storage_backend,
key_rotation_config,
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
)
.await?;
@@ -966,7 +1093,7 @@ where
reply_storage.key_storage(),
reply_controller_sender.clone(),
stats_reporter.clone(),
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
);
// The message_sender is the transmitter for any component generating sphinx packets
@@ -976,7 +1103,8 @@ where
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
gateway_transceiver,
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
EventSender(event_sender),
);
// Channels that the websocket listener can use to signal downstream to the real traffic
@@ -1006,7 +1134,7 @@ where
shared_lane_queue_lengths.clone(),
client_connection_rx,
stats_reporter.clone(),
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
);
if !self
@@ -1022,12 +1150,19 @@ where
shared_topology_accessor.clone(),
message_sender,
stats_reporter.clone(),
&shutdown_tracker.child_tracker(),
&shutdown_tracker.clone(),
);
}
debug!("Core client startup finished!");
debug!("The address of this client is: {self_address}");
tracing::debug!("Core client startup finished!");
tracing::debug!("The address of this client is: {self_address}");
#[cfg(debug_assertions)]
#[cfg(target_arch = "wasm32")]
{
console_log!("Core client startup finished!");
console_log!("Rust::start_base: the address of this client is: {self_address}");
}
Ok(BaseClient {
address: self_address,
@@ -1036,6 +1171,7 @@ where
client_input: ClientInput {
connection_command_sender: client_connection_tx,
input_sender,
client_request_sender,
},
},
client_output: ClientOutputStatus::AwaitingConsumer {
@@ -1051,7 +1187,6 @@ where
},
stats_reporter,
shutdown_handle: shutdown_tracker, // The primary tracker for this client
client_request_sender,
forget_me: self.config.debug.forget_me,
remember_me: self.config.debug.remember_me,
})
@@ -1065,8 +1200,57 @@ pub struct BaseClient {
pub client_output: ClientOutputStatus,
pub client_state: ClientState,
pub stats_reporter: ClientStatsSender,
pub client_request_sender: ClientRequestSender,
pub shutdown_handle: ShutdownTracker,
pub forget_me: ForgetMe,
pub remember_me: RememberMe,
}
#[cfg(test)]
mod tests {
use super::*;
use nym_network_defaults::{ApiUrl, NymNetworkDetails};
#[test]
fn test_network_details_with_multiple_urls() {
// Verify that network details can be configured with multiple API URLs
let mut network_details = NymNetworkDetails::new_empty();
network_details.nym_api_urls = Some(vec![
ApiUrl {
url: "https://validator.nymtech.net/api/".to_string(),
front_hosts: None,
},
ApiUrl {
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
},
]);
assert_eq!(network_details.nym_api_urls.as_ref().unwrap().len(), 2);
assert!(network_details.nym_api_urls.as_ref().unwrap()[1]
.front_hosts
.is_some());
}
#[test]
fn test_network_details_with_front_hosts() {
// Verify that ApiUrl can store domain fronting configuration
let api_url = ApiUrl {
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
};
assert_eq!(api_url.url, "https://nym-frontdoor.vercel.app/api/");
assert_eq!(api_url.front_hosts.as_ref().unwrap().len(), 2);
assert!(api_url
.front_hosts
.as_ref()
.unwrap()
.contains(&"vercel.app".to_string()));
}
#[test]
fn test_default_nym_api_retries_constant() {
// Verify the retry constant is set correctly
assert_eq!(DEFAULT_NYM_API_RETRIES, 3);
}
}
@@ -205,7 +205,7 @@ impl LoopCoverTrafficStream<OsRng> {
TrySendError::Full(_) => {
// This isn't a problem, if the channel is full means we're already sending the
// max amount of messages downstream can handle.
tracing::debug!("Failed to send cover message - channel full");
tracing::trace!("Failed to send cover message - channel full");
}
TrySendError::Closed(_) => {
tracing::warn!("Failed to send cover message - channel closed");
@@ -225,9 +225,15 @@ impl LoopCoverTrafficStream<OsRng> {
// JS: due to identical logical structure to OutQueueControl::on_message(), this is also
// presumably required to prevent bugs in the future. Exact reason is still unknown to me.
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
#[cfg(not(target_arch = "wasm32"))]
tokio::task::yield_now().await;
{
tokio::task::yield_now().await;
}
#[cfg(target_arch = "wasm32")]
{
tokio_with_wasm::task::yield_now().await;
}
}
// it's fine if cover traffic stream task gets killed whilst processing next message
@@ -0,0 +1,40 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use futures::StreamExt;
use crate::client::base_client::{EventReceiver, EventSender, MixnetClientEvent};
/// Launches and manages task events, propagating upwards what is not strictly internal.
pub(crate) struct EventControl {
parent_event_tx: Option<EventSender>,
children_event_rx: EventReceiver,
}
impl EventControl {
pub(crate) fn new(
parent_event_tx: Option<EventSender>,
children_event_rx: EventReceiver,
) -> Self {
EventControl {
parent_event_tx,
children_event_rx,
}
}
fn is_internal(event: MixnetClientEvent) -> bool {
match event {
MixnetClientEvent::Traffic(_) => false,
}
}
pub(crate) async fn run(mut self) {
while let Some(event) = self.children_event_rx.next().await {
if let Some(parent_event_tx) = &self.parent_event_tx {
if !Self::is_internal(event) {
parent_event_tx.send(event);
}
}
}
}
}
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
#![allow(unused_imports)]
use std::time::Duration;
#[cfg(target_arch = "wasm32")]
pub use wasmtimer::{std::Instant, tokio::*};
pub type IntervalStream = gloo_timers::future::IntervalStream;
@@ -1,7 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
use crate::client::{
base_client::{EventSender, MixnetClientEvent},
mix_traffic::transceiver::GatewayTransceiver,
};
use nym_gateway_requests::ClientRequest;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_task::ShutdownToken;
@@ -17,33 +20,41 @@ pub mod transceiver;
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
const MAX_FAILURE_COUNT: usize = 100;
/// Reduced from 100 to 20 to fail fast (~1-2 seconds instead of ~6 seconds).
/// If we can't send 20 packets in a row, the gateway is unreachable.
const MAX_FAILURE_COUNT: usize = 20;
// that's also disgusting.
pub struct Empty;
#[derive(Clone, Copy, Debug)]
pub enum MixTrafficEvent {
FailedSendingSphinx,
}
pub struct MixTrafficController {
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
mix_tx: BatchMixMessageSender,
mix_rx: BatchMixMessageReceiver,
client_rx: ClientRequestReceiver,
client_tx: ClientRequestSender,
// TODO: this is temporary work-around.
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
consecutive_gateway_failure_count: usize,
shutdown_token: ShutdownToken,
event_tx: EventSender,
}
impl MixTrafficController {
pub fn new<T>(
gateway_transceiver: T,
shutdown_token: ShutdownToken,
) -> (
MixTrafficController,
BatchMixMessageSender,
ClientRequestSender,
)
event_tx: EventSender,
) -> MixTrafficController
where
T: GatewayTransceiver + Send + 'static,
{
@@ -52,41 +63,32 @@ impl MixTrafficController {
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(8);
(
MixTrafficController {
gateway_transceiver: Box::new(gateway_transceiver),
mix_rx: message_receiver,
client_rx: client_receiver,
consecutive_gateway_failure_count: 0,
shutdown_token,
},
message_sender,
client_sender,
)
MixTrafficController {
gateway_transceiver: Box::new(gateway_transceiver),
mix_tx: message_sender,
mix_rx: message_receiver,
client_rx: client_receiver,
client_tx: client_sender,
consecutive_gateway_failure_count: 0,
shutdown_token,
event_tx,
}
}
pub fn new_dynamic(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown_token: ShutdownToken,
) -> (
MixTrafficController,
BatchMixMessageSender,
ClientRequestSender,
) {
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(8);
(
MixTrafficController {
gateway_transceiver,
mix_rx: message_receiver,
client_rx: client_receiver,
consecutive_gateway_failure_count: 0,
shutdown_token,
},
message_sender,
client_sender,
)
event_tx: EventSender,
) -> MixTrafficController {
Self::new(gateway_transceiver, shutdown_token, event_tx)
}
pub fn client_tx(&self) -> ClientRequestSender {
self.client_tx.clone()
}
pub fn mix_tx(&self) -> BatchMixMessageSender {
self.mix_tx.clone()
}
async fn on_messages(
@@ -145,34 +147,31 @@ impl MixTrafficController {
trace!("MixTrafficController: Received shutdown");
break;
}
mix_packets = self.mix_rx.recv() => match mix_packets {
Some(mix_packets) => {
if let Err(err) = self.on_messages(mix_packets).await {
error!("Failed to send sphinx packet(s) to the gateway: {err}");
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// Disconnect from the gateway. If we should try to re-connect
// is handled at a higher layer.
error!("Failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead");
// Do we need to handle the embedded mixnet client case
// separately?
break;
}
// mix_rx should never error out as we're holding one instance of the sender
Some(mix_packets) = self.mix_rx.recv() => {
if let Err(err) = self.on_messages(mix_packets).await {
error!("Failed to send sphinx packet(s) to the gateway: {err}");
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// Disconnect from the gateway. If we should try to re-connect
// is handled at a higher layer.
error!("Failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead");
// Do we need to handle the embedded mixnet client case
// separately?
self.event_tx.send(MixnetClientEvent::Traffic(MixTrafficEvent::FailedSendingSphinx));
// IMO it shouldn't be signalled from there but it is how it is
// TODO : report the failure upwards and shutdown from upwards
// Gateway is dead, we have to shut down currently
error!("Signalling shutdown from the MixTrafficController");
self.shutdown_token.cancel();
break;
}
},
None => {
trace!("MixTrafficController: Stopping since channel closed");
break;
}
},
client_request = self.client_rx.recv() => match client_request {
Some(client_request) => {
self.on_client_request(client_request).await;
},
None => {
trace!("MixTrafficController, client request channel closed");
break
}
},
// client_rx should never error out as we're holding one instance of the sender
Some(client_request) = self.client_rx.recv() => {
self.on_client_request(client_request).await;
}
}
}
debug!("MixTrafficController: Exiting");
+1
View File
@@ -3,6 +3,7 @@
pub mod base_client;
pub mod cover_traffic_stream;
pub(crate) mod event_control;
pub(crate) mod helpers;
pub mod inbound_messages;
pub mod key_manager;
@@ -31,9 +31,9 @@ use tracing::*;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::{sleep, Sleep};
// use wasm_utils::console_log;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::{sleep, Sleep};
mod sending_delay_controller;
/// Configurable parameters of the `OutQueueControl`
@@ -298,6 +298,8 @@ where
"failed to send mixnet packet due to closed channel (outside of shutdown!)"
);
}
// Early return to avoid further processing when channel is closed
return;
}
Ok(_) => {
let event = if fragment_id.is_some() {
@@ -325,9 +327,15 @@ where
// ready and hence was immediately re-scheduled causing other tasks to be starved;
// yield makes it go back the scheduling queue regardless of its value availability
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
#[cfg(not(target_arch = "wasm32"))]
tokio::task::yield_now().await;
{
tokio::task::yield_now().await;
}
#[cfg(target_arch = "wasm32")]
{
tokio_with_wasm::task::yield_now().await;
}
}
fn on_close_connection(&mut self, connection_id: ConnectionId) {
+1 -1
View File
@@ -45,7 +45,7 @@ pub enum ClientCoreError {
#[cfg(not(target_arch = "wasm32"))]
#[error("resolution failed: {0}")]
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
ResolutionFailed(#[from] nym_http_api_client::ResolveError),
#[error("no gateways on network")]
NoGatewaysOnNetwork,
+77 -20
View File
@@ -45,6 +45,7 @@ type WsConn = JSWebsocket;
const CONCURRENT_GATEWAYS_MEASURED: usize = 20;
const MEASUREMENTS: usize = 3;
const DEFAULT_NYM_API_RETRIES: usize = 3;
#[cfg(not(target_arch = "wasm32"))]
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
@@ -132,25 +133,27 @@ impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
}
}
pub async fn gateways_for_init<R: Rng>(
rng: &mut R,
pub async fn gateways_for_init(
nym_apis: &[Url],
user_agent: Option<UserAgent>,
minimum_performance: u8,
ignore_epoch_roles: bool,
retry_count: Option<usize>,
) -> Result<Vec<RoutingNode>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
// Build client with ALL URLs for fallback support
let nym_api_urls: Vec<nym_http_api_client::Url> = nym_apis
.iter()
.map(|url| nym_http_api_client::Url::from(url.clone()))
.collect();
// Use the unified HTTP client directly with optional user agent
let mut builder = nym_http_api_client::Client::builder(nym_api.clone())
.map_err(|e| {
ClientCoreError::ValidatorClientError(nym_validator_client::ValidatorClientError::from(
e,
))
})?
.with_bincode(); // Use bincode for better performance
if nym_api_urls.is_empty() {
return Err(ClientCoreError::ListOfNymApisIsEmpty);
}
let retry_count = retry_count.unwrap_or(DEFAULT_NYM_API_RETRIES);
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())?
.with_retries(retry_count)
.with_bincode();
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
@@ -160,7 +163,7 @@ pub async fn gateways_for_init<R: Rng>(
ClientCoreError::ValidatorClientError(nym_validator_client::ValidatorClientError::from(e))
})?;
tracing::debug!("Fetching list of gateways from: {nym_api}");
tracing::debug!("Fetching list of gateways from: {:?}", nym_api_urls);
// Use our helper to handle pagination
let gateways = get_all_basic_entry_nodes_with_metadata(&client, true)
@@ -172,17 +175,15 @@ pub async fn gateways_for_init<R: Rng>(
// filter out gateways below minimum performance and ones that could operate as a mixnode
// (we don't want instability)
let valid_gateways = gateways
let valid_gateways: Vec<RoutingNode> = gateways
.iter()
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>();
tracing::debug!("After checking validity: {}", valid_gateways.len());
tracing::trace!("Valid gateways: {valid_gateways:#?}");
.collect();
tracing::info!(
"and {} after validity and performance filtering",
"Found {} valid gateways after filtering",
valid_gateways.len()
);
@@ -345,13 +346,20 @@ pub(super) fn get_specified_gateway(
must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> {
tracing::debug!("Requesting specified gateway: {gateway_identity}");
let user_gateway = ed25519::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let gateway = gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
.ok_or_else(|| {
tracing::debug!(
"Gateway {gateway_identity} not found in {} available gateways",
gateways.len()
);
ClientCoreError::NoGatewayWithId(gateway_identity.to_string())
})?;
let Some(entry_details) = gateway.entry.as_ref() else {
return Err(ClientCoreError::UnsupportedEntry {
@@ -414,3 +422,52 @@ pub(super) async fn register_with_gateway(
authenticated_ephemeral_client: gateway_client,
})
}
#[cfg(test)]
mod tests {
use url::Url;
#[test]
fn test_single_url_builds_without_retries() {
let urls = [Url::parse("https://api.nym.com").unwrap()];
let nym_api_urls: Vec<nym_http_api_client::Url> = urls
.iter()
.map(|url| nym_http_api_client::Url::from(url.clone()))
.collect();
assert_eq!(nym_api_urls.len(), 1, "Should have exactly one URL");
}
#[test]
fn test_multiple_urls_prepared_for_retries() {
let urls = [
Url::parse("https://api1.nym.com").unwrap(),
Url::parse("https://api2.nym.com").unwrap(),
Url::parse("https://api3.nym.com").unwrap(),
];
let nym_api_urls: Vec<nym_http_api_client::Url> = urls
.iter()
.map(|url| nym_http_api_client::Url::from(url.clone()))
.collect();
assert_eq!(nym_api_urls.len(), 3, "Should have all three URLs");
assert!(
nym_api_urls.len() > 1,
"Multiple URLs trigger retry behavior"
);
}
#[test]
fn test_empty_url_list_is_detected() {
let urls: Vec<Url> = vec![];
let nym_api_urls: Vec<nym_http_api_client::Url> = urls
.iter()
.map(|url| nym_http_api_client::Url::from(url.clone()))
.collect();
assert!(nym_api_urls.is_empty(), "Empty list should remain empty");
}
}
@@ -30,7 +30,6 @@ pub(crate) async fn connect_async(
resolver
.resolve_str(domain)
.await?
.into_iter()
.map(|a| SocketAddr::new(a, port))
.collect()
}
+3 -1
View File
@@ -24,7 +24,9 @@ pub fn spawn_future<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
wasm_bindgen_futures::spawn_local(future);
wasm_bindgen_futures::spawn_local(async move {
future.await;
});
}
#[deprecated(note = "use spawn_future from nym_task crate instead")]
@@ -39,7 +39,6 @@ pub(crate) async fn connect_async(
resolver
.resolve_str(domain)
.await?
.into_iter()
.map(|a| SocketAddr::new(a, port))
.collect()
}
@@ -54,7 +54,7 @@ pub enum GatewayClientError {
#[cfg(not(target_arch = "wasm32"))]
#[error("resolution failed: {0}")]
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
ResolutionFailed(#[from] nym_http_api_client::ResolveError),
#[error("No shared key was provided or obtained")]
NoSharedKeyAvailable,
@@ -3,7 +3,7 @@ name = "nym-validator-client"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.56"
rust-version = "1.85"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,7 +10,7 @@ use cosmrs::tx;
use cosmrs::tx::SignDoc;
use nym_config::defaults;
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
type Secp256k1Keypair = (SigningKey, PublicKey);
@@ -128,9 +128,20 @@ impl DirectSecp256k1HdWallet {
Ok(accounts)
}
pub fn secret(&self) -> &bip39::Mnemonic {
&self.secret
}
#[deprecated(
note = "use either .secret() for obtaining &bip39::Mnemonic or .mnemonic_string() for Zeroizing wrapper around the String"
)]
pub fn mnemonic(&self) -> String {
self.secret.to_string()
}
pub fn mnemonic_string(&self) -> Zeroizing<String> {
Zeroizing::new(self.secret.to_string())
}
}
#[must_use]
@@ -18,6 +18,6 @@ pub fn create_account(args: Args, prefix: &str) {
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
// Output address and mnemonics into separate lines for easier parsing
println!("{}", wallet.mnemonic());
println!("{}", wallet.mnemonic_string().as_str());
println!("{}", wallet.try_derive_accounts().unwrap()[0].address());
}
@@ -241,23 +241,28 @@ impl Epoch {
//
// Note: It's important that the variant ordering is not changed otherwise it would mess up the derived `PartialOrd`
#[cw_serde]
#[derive(Copy)]
#[derive(Copy, Default)]
pub enum EpochState {
#[default]
WaitingInitialisation,
PublicKeySubmission { resharing: bool },
DealingExchange { resharing: bool },
VerificationKeySubmission { resharing: bool },
VerificationKeyValidation { resharing: bool },
VerificationKeyFinalization { resharing: bool },
PublicKeySubmission {
resharing: bool,
},
DealingExchange {
resharing: bool,
},
VerificationKeySubmission {
resharing: bool,
},
VerificationKeyValidation {
resharing: bool,
},
VerificationKeyFinalization {
resharing: bool,
},
InProgress,
}
impl Default for EpochState {
fn default() -> Self {
Self::WaitingInitialisation
}
}
impl Display for EpochState {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
@@ -1,17 +1,17 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::testing::{message_info, MockApi, MockQuerier, MockStorage};
use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, message_info};
use cosmwasm_std::{
coins, Addr, BankMsg, CosmosMsg, Decimal, Empty, Env, MemoryStorage, MessageInfo, Order,
OwnedDeps, Response, StdResult, Storage,
Addr, BankMsg, CosmosMsg, Decimal, Empty, Env, MemoryStorage, MessageInfo, Order, OwnedDeps,
Response, StdResult, Storage, coins,
};
use cw_storage_plus::{KeyDeserialize, Map, Prefix, PrimaryKey};
use nym_contracts_common::events::may_find_attribute;
use rand::{RngCore, SeedableRng};
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt::Debug;
use std::str::FromStr;
@@ -4,12 +4,12 @@
use crate::{ContractTester, TestableNymContract};
use cosmwasm_std::testing::{message_info, mock_env};
use cosmwasm_std::{
from_json, Addr, BlockInfo, Coin, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response,
StdResult, Storage, Timestamp,
Addr, BlockInfo, Coin, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
Storage, Timestamp, from_json,
};
use cw_multi_test::{next_block, AppResponse, Executor};
use serde::de::DeserializeOwned;
use cw_multi_test::{AppResponse, Executor, next_block};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::any::type_name;
use std::fmt::Debug;
@@ -2,18 +2,18 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{
CommonStorageKeys, ContractOpts, ContractTester, StorageWrapper, TestableNymContract,
TEST_DENOM,
CommonStorageKeys, ContractOpts, ContractTester, StorageWrapper, TEST_DENOM,
TestableNymContract,
};
use cosmwasm_std::testing::message_info;
use cosmwasm_std::{
coin, coins, from_json, to_json_vec, Addr, Coin, MessageInfo, StdError, StdResult, Storage,
Addr, Coin, MessageInfo, StdError, StdResult, Storage, coin, coins, from_json, to_json_vec,
};
use cw_multi_test::Executor;
use cw_storage_plus::{Key, Path, PrimaryKey};
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::any::type_name;
use std::ops::Deref;
@@ -1,16 +1,16 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{mock_api, test_rng, TEST_DENOM};
use crate::{TEST_DENOM, mock_api, test_rng};
use cosmwasm_std::testing::MockApi;
use cosmwasm_std::{
coin, coins, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, QuerierWrapper,
Record, Response, Storage,
Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, QuerierWrapper, Record, Response,
Storage, coin, coins,
};
use cw_multi_test::{App, AppBuilder, BankKeeper, Contract, ContractWrapper, Executor};
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::marker::PhantomData;
@@ -1,9 +1,9 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{from_json, Binary, CustomQuery, QuerierWrapper, StdResult};
use serde::de::DeserializeOwned;
use cosmwasm_std::{Binary, CustomQuery, QuerierWrapper, StdResult, from_json};
use serde::Serialize;
use serde::de::DeserializeOwned;
// re-expose methods from QuerierWrapper as traits so that we could more easily define extension traits
pub trait ContractQuerier {
@@ -1,10 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{from_json, to_json_vec, Addr, Coin, MessageInfo, StdResult};
use cosmwasm_std::{Addr, Coin, MessageInfo, StdResult, from_json, to_json_vec};
use schemars::JsonSchema;
use serde::de::DeserializeOwned;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
pub use verifier::Verifier;
@@ -2,7 +2,7 @@
name = "nym-mixnet-contract-common"
version = "0.6.0"
description = "Common library for the Nym mixnet contract"
rust-version = "1.62"
rust-version = "1.85"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
@@ -5,8 +5,8 @@ use crate::nym_node::Role;
use crate::{
EpochEventId, EpochState, IntervalEventId, NodeId, OperatingCostRange, ProfitMarginRange,
};
use contracts_common::signing::verifier::ApiVerifierError;
use contracts_common::Percent;
use contracts_common::signing::verifier::ApiVerifierError;
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
use cw_controllers::AdminError;
use thiserror::Error;
@@ -47,7 +47,9 @@ pub enum MixnetContractError {
)]
InvalidPubKey,
#[error("Attempted to reduce node pledge ({current}{denom} - {decrease_by}{denom}) below the minimum amount: {minimum}{denom}")]
#[error(
"Attempted to reduce node pledge ({current}{denom} - {decrease_by}{denom}) below the minimum amount: {minimum}{denom}"
)]
InvalidPledgeReduction {
current: Uint128,
decrease_by: Uint128,
@@ -123,7 +125,9 @@ pub enum MixnetContractError {
#[error("Provided ed25519 signature did not verify correctly")]
InvalidEd25519Signature,
#[error("Can't perform the specified action as the current epoch is still progress. It started at {epoch_start} and finishes at {epoch_end}, while the current block time is {current_block_time}")]
#[error(
"Can't perform the specified action as the current epoch is still progress. It started at {epoch_start} and finishes at {epoch_end}, while the current block time is {current_block_time}"
)]
EpochInProgress {
current_block_time: u64,
epoch_start: i64,
@@ -133,7 +137,9 @@ pub enum MixnetContractError {
#[error("attempted to reward a gateway node - this has not been fully integrated yet")]
GatewayRewarding,
#[error("node {node_id} has already been rewarded during the current rewarding epoch ({absolute_epoch_id})")]
#[error(
"node {node_id} has already been rewarded during the current rewarding epoch ({absolute_epoch_id})"
)]
NodeAlreadyRewarded {
node_id: NodeId,
absolute_epoch_id: u32,
@@ -172,7 +178,9 @@ pub enum MixnetContractError {
#[error("one of the roles in the new active set is empty")]
EmptyRoleAssignment,
#[error("the number of mixnodes in the rewarded set is not divisible by the number of mix-layers (3)")]
#[error(
"the number of mixnodes in the rewarded set is not divisible by the number of mix-layers (3)"
)]
UnevenLayerAssignment,
#[error("provided active set is bigger than the rewarded set")]
@@ -196,25 +204,35 @@ pub enum MixnetContractError {
#[error("key rotation validity below minimum value")]
TooShortRotationInterval,
#[error("this validator ({current_validator}) is not the one responsible for advancing this epoch. It's responsibility of {chosen_validator}.")]
#[error(
"this validator ({current_validator}) is not the one responsible for advancing this epoch. It's responsibility of {chosen_validator}."
)]
RewardingValidatorMismatch {
current_validator: Addr,
chosen_validator: Addr,
},
#[error("the epoch is currently in the process of being advanced. (the state is {current_state}) Please try sending your transaction again once this has finished")]
#[error(
"the epoch is currently in the process of being advanced. (the state is {current_state}) Please try sending your transaction again once this has finished"
)]
EpochAdvancementInProgress { current_state: EpochState },
#[error("the epoch is in an unexpected state. expected 'mix rewarding' state, but we're in {current_state} instead.")]
#[error(
"the epoch is in an unexpected state. expected 'mix rewarding' state, but we're in {current_state} instead."
)]
UnexpectedNonRewardingEpochState { current_state: EpochState },
#[error("attempted to reward mixnode out of order. Attempted to reward {attempted_to_reward} while last rewarded was {last_rewarded}.")]
#[error(
"attempted to reward mixnode out of order. Attempted to reward {attempted_to_reward} while last rewarded was {last_rewarded}."
)]
RewardingOutOfOrder {
last_rewarded: NodeId,
attempted_to_reward: NodeId,
},
#[error("the epoch is currently not in the 'event reconciliation' state. (the state is {current_state})")]
#[error(
"the epoch is currently not in the 'event reconciliation' state. (the state is {current_state})"
)]
EpochNotInEventReconciliationState { current_state: EpochState },
#[error(
@@ -225,14 +243,18 @@ pub enum MixnetContractError {
#[error("unexpected role assignment. got: {got} while expected: {expected}")]
UnexpectedRoleAssignment { expected: Role, got: Role },
#[error("attempted to assign an invalid number of nodes for a role of {role}. got {assigned}, but the maximum allowed is {allowed}")]
#[error(
"attempted to assign an invalid number of nodes for a role of {role}. got {assigned}, but the maximum allowed is {allowed}"
)]
IllegalRoleCount {
role: Role,
assigned: u32,
allowed: u32,
},
#[error("the epoch is currently not in the 'epoch advancement' state. (the state is {current_state})")]
#[error(
"the epoch is currently not in the 'epoch advancement' state. (the state is {current_state})"
)]
EpochNotInAdvancementState { current_state: EpochState },
#[error("failed to verify message signature: {source}")]
@@ -241,7 +263,9 @@ pub enum MixnetContractError {
source: ApiVerifierError,
},
#[error("this operation is no longer allowed to be performed with vesting tokens. please move them to your liquid balance and try again")]
#[error(
"this operation is no longer allowed to be performed with vesting tokens. please move them to your liquid balance and try again"
)]
DisabledVestingOperation,
#[error(
@@ -249,7 +273,9 @@ pub enum MixnetContractError {
)]
NotAVestingMixnode,
#[error("this delegation has not been performed with the vesting tokens or has already been migrated")]
#[error(
"this delegation has not been performed with the vesting tokens or has already been migrated"
)]
NotAVestingDelegation,
#[error("the provided profit margin ({provided}) is outside the allowed range: {range}")]
@@ -258,7 +284,9 @@ pub enum MixnetContractError {
range: ProfitMarginRange,
},
#[error("the provided interval operating cost ({provided}{denom}) is outside the allowed range: {range}")]
#[error(
"the provided interval operating cost ({provided}{denom}) is outside the allowed range: {range}"
)]
OperatingCostOutsideRange {
denom: String,
provided: Uint128,
@@ -279,7 +307,9 @@ pub enum MixnetContractError {
#[error("the provided nym-node version is not a valid semver. got: {provided}")]
InvalidNymNodeSemver { provided: String },
#[error("the provided nym-node version is not greater than the current one. got: {provided}. current: {current}")]
#[error(
"the provided nym-node version is not greater than the current one. got: {provided}. current: {current}"
)]
NonIncreasingSemver { provided: String, current: String },
}
@@ -9,7 +9,7 @@ use crate::reward_params::{ActiveSetUpdate, IntervalRewardParams, IntervalReward
use crate::rewarding::RewardDistribution;
use crate::{BlockHeight, ContractStateParamsUpdate, EpochId, IdentityKeyRef, Interval, NodeId};
pub use contracts_common::events::*;
use cosmwasm_std::{attr, Addr, Coin, Decimal, Event};
use cosmwasm_std::{Addr, Coin, Decimal, Event, attr};
use std::fmt::Display;
pub const EVENT_VERSION_PREFIX: &str = "v2_";
@@ -3,7 +3,7 @@
use crate::{IdentityKey, NodeId, SphinxKey};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{to_json_string, Addr, Coin};
use cosmwasm_std::{Addr, Coin, to_json_string};
use std::cmp::Ordering;
use std::fmt::Display;
@@ -1,13 +1,13 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::NodeId;
use crate::error::MixnetContractError;
use crate::nym_node::Role;
use crate::NodeId;
use cosmwasm_schema::cw_serde;
use cosmwasm_schema::schemars::gen::SchemaGenerator;
use cosmwasm_schema::schemars::schema::{InstanceType, Schema, SchemaObject};
use cosmwasm_schema::schemars::JsonSchema;
use cosmwasm_schema::schemars::r#gen::SchemaGenerator;
use cosmwasm_schema::schemars::schema::{InstanceType, Schema, SchemaObject};
use cosmwasm_std::{Addr, Env};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
@@ -27,8 +27,8 @@ pub(crate) mod string_rfc3339_offset_date_time {
use serde::ser::Error;
use serde::{Deserializer, Serialize, Serializer};
use std::fmt::Formatter;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;
struct Rfc3339OffsetDateTimeVisitor;
@@ -91,7 +91,7 @@ impl EpochStatus {
) -> Result<bool, MixnetContractError> {
match &mut self.state {
EpochState::Rewarding {
ref mut last_rewarded,
last_rewarded,
final_node_id,
} => {
if new_last_rewarded <= *last_rewarded {
@@ -254,7 +254,7 @@ impl JsonSchema for Interval {
"Interval".to_owned()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
fn json_schema(r#gen: &mut SchemaGenerator) -> Schema {
let mut schema_object = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..SchemaObject::default()
@@ -263,12 +263,13 @@ impl JsonSchema for Interval {
let object_validation = schema_object.object();
object_validation
.properties
.insert("id".to_owned(), gen.subschema_for::<IntervalId>());
.insert("id".to_owned(), r#gen.subschema_for::<IntervalId>());
object_validation.required.insert("id".to_owned());
object_validation
.properties
.insert("epochs_in_interval".to_owned(), gen.subschema_for::<u32>());
object_validation.properties.insert(
"epochs_in_interval".to_owned(),
r#gen.subschema_for::<u32>(),
);
object_validation
.required
.insert("epochs_in_interval".to_owned());
@@ -277,7 +278,7 @@ impl JsonSchema for Interval {
// serialization to string, so we just specify the schema to be String.
object_validation.properties.insert(
"current_epoch_start".to_owned(),
gen.subschema_for::<String>(),
r#gen.subschema_for::<String>(),
);
object_validation
.required
@@ -285,7 +286,7 @@ impl JsonSchema for Interval {
object_validation.properties.insert(
"current_epoch_id".to_owned(),
gen.subschema_for::<EpochId>(),
r#gen.subschema_for::<EpochId>(),
);
object_validation
.required
@@ -293,12 +294,12 @@ impl JsonSchema for Interval {
object_validation
.properties
.insert("epoch_length".to_owned(), gen.subschema_for::<Duration>());
.insert("epoch_length".to_owned(), r#gen.subschema_for::<Duration>());
object_validation.required.insert("epoch_length".to_owned());
object_validation.properties.insert(
"total_elapsed_epochs".to_owned(),
gen.subschema_for::<EpochId>(),
r#gen.subschema_for::<EpochId>(),
);
object_validation
.required
@@ -9,14 +9,14 @@ use crate::error::MixnetContractError;
use crate::helpers::IntoBaseDecimal;
use crate::nym_node::Role;
use crate::reward_params::{NodeRewardingParameters, RewardingParams};
use crate::rewarding::helpers::truncate_reward;
use crate::rewarding::RewardDistribution;
use crate::rewarding::helpers::truncate_reward;
use crate::{
Delegation, EpochEventId, EpochId, IdentityKey, IntervalEventId, NodeId, OperatingCostRange,
Percent, ProfitMarginRange, SphinxKey,
};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{to_json_string, Addr, Coin, Decimal, StdResult, Uint128};
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128, to_json_string};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -18,7 +18,7 @@ use crate::{
VersionScoreFormulaParams,
};
use crate::{OperatingCostRange, ProfitMarginRange};
use contracts_common::{signing::MessageSignature, IdentityKey, Percent};
use contracts_common::{IdentityKey, Percent, signing::MessageSignature};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Coin, Decimal};
use std::time::Duration;
@@ -55,7 +55,7 @@ use crate::{
types::{ContractState, ContractStateParams},
};
#[cfg(feature = "schema")]
use contracts_common::{signing::Nonce, ContractBuildInformation};
use contracts_common::{ContractBuildInformation, signing::Nonce};
#[cfg(feature = "schema")]
use cosmwasm_schema::QueryResponses;
@@ -3,9 +3,9 @@
use crate::helpers::IntoBaseDecimal;
use crate::nym_node::Role;
use crate::{error::MixnetContractError, Percent};
use crate::{Percent, error::MixnetContractError};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{to_json_string, Decimal};
use cosmwasm_std::{Decimal, to_json_string};
pub type Performance = Percent;
pub type WorkFactor = Decimal;
@@ -4,8 +4,8 @@
use crate::error::MixnetContractError;
use crate::helpers::IntoBaseDecimal;
use crate::reward_params::{NodeRewardingParameters, WorkFactor};
use crate::rewarding::simulator::simulated_node::SimulatedNode;
use crate::rewarding::RewardDistribution;
use crate::rewarding::simulator::simulated_node::SimulatedNode;
use crate::{Delegation, Interval, IntervalRewardParams, NodeCostParams, NodeId, RewardingParams};
use cosmwasm_std::{Coin, Decimal};
use std::collections::BTreeMap;
@@ -226,9 +226,9 @@ impl Simulator {
#[cfg(test)]
mod tests {
use super::*;
use crate::Percent;
use crate::helpers::compare_decimals;
use crate::reward_params::RewardedSetParams;
use crate::Percent;
use cosmwasm_std::testing::mock_env;
use std::time::Duration;
@@ -1,10 +1,10 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::EpochId;
use crate::config_score::{ConfigScoreParams, OutdatedVersionWeights, VersionScoreFormulaParams};
use crate::nym_node::Role;
use crate::reward_params::RewardedSetParams;
use crate::EpochId;
use contracts_common::Percent;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
@@ -23,7 +23,9 @@ pub enum NymPerformanceContractError {
#[error("{address} is not an authorised network monitor")]
NotAuthorised { address: Addr },
#[error("attempted to submit performance data for epoch {epoch_id} and node {node_id} whilst last submitted was {last_epoch_id} for node {last_node_id}")]
#[error(
"attempted to submit performance data for epoch {epoch_id} and node {node_id} whilst last submitted was {last_epoch_id} for node {last_node_id}"
)]
StalePerformanceSubmission {
epoch_id: EpochId,
node_id: NodeId,
@@ -16,7 +16,9 @@ pub enum NymPoolContractError {
#[error(transparent)]
StdErr(#[from] cosmwasm_std::StdError),
#[error("this sender is not authorised to revoke this grant. its neither the admin or the original (and still whitelisted) granter")]
#[error(
"this sender is not authorised to revoke this grant. its neither the admin or the original (and still whitelisted) granter"
)]
UnauthorizedGrantRevocation,
#[error("the specified address is already a whitelisted granter")]
@@ -28,7 +30,9 @@ pub enum NymPoolContractError {
#[error("invalid coin denomination. got {got}, but expected {expected}")]
InvalidDenom { expected: String, got: String },
#[error("there already exists an active grant for {grantee}. it was granted by {granter} at block height {created_at_height}")]
#[error(
"there already exists an active grant for {grantee}. it was granted by {granter} at block height {created_at_height}"
)]
GrantAlreadyExist {
granter: String,
grantee: String,
@@ -38,13 +42,17 @@ pub enum NymPoolContractError {
#[error("could not find any active grants for {grantee}")]
GrantNotFound { grantee: String },
#[error("the provided timestamp value ({timestamp}) is set in the past. the current block timestamp is {current_block_timestamp}")]
#[error(
"the provided timestamp value ({timestamp}) is set in the past. the current block timestamp is {current_block_timestamp}"
)]
TimestampInThePast {
timestamp: u64,
current_block_timestamp: u64,
},
#[error("there are not enough tokens to process this request. {available} are available, but {required} is needed.")]
#[error(
"there are not enough tokens to process this request. {available} are available, but {required} is needed."
)]
InsufficientTokens { available: Coin, required: Coin },
#[error("the period length can't be zero")]
@@ -53,22 +61,30 @@ pub enum NymPoolContractError {
#[error("the provided coin value is zero")]
ZeroAmount,
#[error("the periodic spend limit of {periodic} was set to be higher than the total spend limit {total_limit}")]
#[error(
"the periodic spend limit of {periodic} was set to be higher than the total spend limit {total_limit}"
)]
PeriodicGrantOverSpendLimit { periodic: Coin, total_limit: Coin },
#[error("the accumulation spend limit of {accumulation} was set to be lower than the periodic grant amount of {periodic_grant}")]
#[error(
"the accumulation spend limit of {accumulation} was set to be lower than the periodic grant amount of {periodic_grant}"
)]
AccumulationBelowGrantAmount {
accumulation: Coin,
periodic_grant: Coin,
},
#[error("the accumulation spend limit of {accumulation} was set to be higher than the total spend limit of {total_limit}")]
#[error(
"the accumulation spend limit of {accumulation} was set to be higher than the total spend limit of {total_limit}"
)]
AccumulationOverSpendLimit {
accumulation: Coin,
total_limit: Coin,
},
#[error("the specified delayed allowance would never be available. it would become active at {available_timestamp} yet it expires at {expiration_timestamp}")]
#[error(
"the specified delayed allowance would never be available. it would become active at {available_timestamp} yet it expires at {expiration_timestamp}"
)]
UnattainableDelayedAllowance {
expiration_timestamp: u64,
available_timestamp: u64,
@@ -88,10 +88,10 @@ pub mod grants {
pub fn basic_mut(&mut self) -> &mut BasicAllowance {
match self {
Allowance::Basic(ref mut allowance) => allowance,
Allowance::ClassicPeriodic(ref mut allowance) => &mut allowance.basic,
Allowance::CumulativePeriodic(ref mut allowance) => &mut allowance.basic,
Allowance::Delayed(ref mut allowance) => &mut allowance.basic,
Allowance::Basic(allowance) => allowance,
Allowance::ClassicPeriodic(allowance) => &mut allowance.basic,
Allowance::CumulativePeriodic(allowance) => &mut allowance.basic,
Allowance::Delayed(allowance) => &mut allowance.basic,
}
}
@@ -752,7 +752,7 @@ pub mod query_responses {
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::{coin, Uint128};
use cosmwasm_std::{Uint128, coin};
const TEST_DENOM: &str = "unym";
@@ -873,8 +873,8 @@ mod tests {
#[cfg(test)]
mod basic_allowance {
use super::*;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::Timestamp;
use cosmwasm_std::testing::mock_env;
#[test]
fn doesnt_allow_expirations_in_the_past() {
@@ -1158,8 +1158,8 @@ mod tests {
#[cfg(test)]
mod delayed_allowance {
use super::*;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::Timestamp;
use cosmwasm_std::testing::mock_env;
#[test]
fn doesnt_allow_availability_in_the_past() {
@@ -20,8 +20,8 @@ pub fn ensure_unix_timestamp_not_in_the_past(
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::Timestamp;
use cosmwasm_std::testing::mock_env;
use time::macros::datetime;
#[test]
@@ -61,7 +61,9 @@ pub enum VestingContractError {
#[error("VESTING ({l}): No bond found for account {0}", l = line!())]
NoBondFound(String),
#[error("VESTING: Attempted to reduce mixnode bond pledge below zero! The current pledge is {current} and we attempted to reduce it by {decrease_by}.")]
#[error(
"VESTING: Attempted to reduce mixnode bond pledge below zero! The current pledge is {current} and we attempted to reduce it by {decrease_by}."
)]
InvalidBondPledgeReduction { current: Coin, decrease_by: Coin },
#[error("VESTING ({l}): Action can only be executed by account owner -> {0}", l = line!())]
@@ -85,13 +87,17 @@ pub enum VestingContractError {
#[error("VESTING: ({l}: Account owned by {owner} has unpopulated vesting periods!", l = line!())]
UnpopulatedVestingPeriods { owner: Addr },
#[error("VESTING: Vesting account associated with {0} already exists, only addresses with not existing vesting accounts can be added as staking addresses")]
#[error(
"VESTING: Vesting account associated with {0} already exists, only addresses with not existing vesting accounts can be added as staking addresses"
)]
StakingAccountExists(String),
#[error("VESTING: {address} is not permitted to perform staking on behalf of {for_account}")]
InvalidStakingAccount { address: Addr, for_account: Addr },
#[error("VESTING: {address} ({acc_id} has already performed {num} individual delegations towards {mix_id}. No further delegations are allowed. Please consider consolidating those delegations instead. The current cap is {cap}.")]
#[error(
"VESTING: {address} ({acc_id} has already performed {num} individual delegations towards {mix_id}. No further delegations are allowed. Please consider consolidating those delegations instead. The current cap is {cap}."
)]
TooManyDelegations {
address: Addr,
acc_id: VestingAccountStorageKey,
@@ -6,9 +6,9 @@ use contracts_common::signing::MessageSignature;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Coin, Timestamp};
use mixnet_contract_common::{
Gateway, MixNode, NodeId,
gateway::GatewayConfigUpdate,
mixnode::{MixNodeConfigUpdate, NodeCostParams},
Gateway, MixNode, NodeId,
};
#[cfg(feature = "schema")]
@@ -18,10 +18,10 @@ use cosmwasm_schema::QueryResponses;
#[cfg(feature = "schema")]
use crate::{
account::Account,
types::{Period, PledgeData, VestingDelegation},
AccountsResponse, AllDelegationsResponse, DelegationTimesResponse, OriginalVestingResponse,
VestingCoinsResponse,
account::Account,
types::{Period, PledgeData, VestingDelegation},
};
#[cw_serde]
+1 -1
View File
@@ -8,7 +8,7 @@ async fn main() -> anyhow::Result<()> {
use sqlx::{Connection, SqliteConnection};
use std::env;
let out_dir = env::var("OUT_DIR")?;
let out_dir = env::var("OUT_DIR").context("missing OUT_DIR env variable")?;
let database_path = format!("{out_dir}/nym-credential-proxy-example.sqlite");
// remove the db file if it already existed from previous build
@@ -118,7 +118,9 @@ pub async fn make_deposits_request(
// that one is tricky. deposits technically got made, but we somehow failed to parse response,
// in this case terminate the proxy with 0 exit code so it wouldn't get automatically restarted
// because it requires some serious MANUAL intervention
error!("CRITICAL FAILURE: failed to parse out deposit information from the contract transaction. either the chain got upgraded and the schema changed or the ecash contract got changed! terminating the process. it has to be inspected manually. error was: {err}");
error!(
"CRITICAL FAILURE: failed to parse out deposit information from the contract transaction. either the chain got upgraded and the schema changed or the ecash contract got changed! terminating the process. it has to be inspected manually. error was: {err}"
);
cancellation_on_critical_failure.cancel();
return Err(CredentialProxyError::DepositFailure);
}
@@ -126,7 +128,10 @@ pub async fn make_deposits_request(
if contract_data.len() != amount {
// another critical failure, that one should be quite impossible and thus has to be manually inspected
error!("CRITICAL FAILURE: failed to parse out all deposit information from the contract transaction. got {} responses while we sent {amount} deposits! either the chain got upgraded and the schema changed or the ecash contract got changed! terminating the process. it has to be inspected manually", contract_data.len());
error!(
"CRITICAL FAILURE: failed to parse out all deposit information from the contract transaction. got {} responses while we sent {amount} deposits! either the chain got upgraded and the schema changed or the ecash contract got changed! terminating the process. it has to be inspected manually",
contract_data.len()
);
cancellation_on_critical_failure.cancel();
return Err(CredentialProxyError::DepositFailure);
}
@@ -138,7 +143,9 @@ pub async fn make_deposits_request(
Ok(deposit_id) => deposit_id,
Err(err) => {
// another impossibility
error!("CRITICAL FAILURE: failed to parse out deposit id out of the response at index {response_index}: {err}. either the chain got upgraded and the schema changed or the ecash contract got changed! terminating the process. it has to be inspected manually");
error!(
"CRITICAL FAILURE: failed to parse out deposit id out of the response at index {response_index}: {err}. either the chain got upgraded and the schema changed or the ecash contract got changed! terminating the process. it has to be inspected manually"
);
cancellation_on_critical_failure.cancel();
return Err(CredentialProxyError::DepositFailure);
}
@@ -17,7 +17,7 @@ use tokio_util::sync::CancellationToken;
use tracing::{debug, info, instrument, warn};
use uuid::Uuid;
pub use helpers::{make_deposits_request, split_deposits, BufferedDeposit, PerformedDeposits};
pub use helpers::{BufferedDeposit, PerformedDeposits, make_deposits_request, split_deposits};
pub(crate) mod helpers;
mod refill_task;
@@ -219,7 +219,9 @@ impl DepositsBuffer {
match maybe_deposit {
None => {
warn!("we currently don't have any usable deposits! are we using them up faster than we request them?");
warn!(
"we currently don't have any usable deposits! are we using them up faster than we request them?"
);
// we have to wait until refill task has completed (either initiated by this or another fn call)
self.wait_for_deposit(request_uuid, requested_on, client_pubkey)
@@ -242,7 +244,9 @@ impl DepositsBuffer {
let task_handle = self.inner.deposits_refill_task.take_task_join_handle();
if let Some(task_handle) = task_handle {
if !task_handle.is_finished() {
info!("the deposit refill task is currently in progress - waiting for the current transaction to finish before concluding shutdown");
info!(
"the deposit refill task is currently in progress - waiting for the current transaction to finish before concluding shutdown"
);
let _ = task_handle.await;
}
}
@@ -42,7 +42,9 @@ impl RefillTask {
if let Some(existing_handle) = guard.as_ref() {
if !existing_handle.is_finished() {
error!("CRITICAL BUG: there was already a deposit refill task spawned that hasn't yet finished")
error!(
"CRITICAL BUG: there was already a deposit refill task spawned that hasn't yet finished"
)
}
}
+18 -5
View File
@@ -3,7 +3,7 @@
use nym_ecash_signer_check::SignerCheckError;
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::{error::NymAPIError, EpochId};
use nym_validator_client::nym_api::{EpochId, error::NymAPIError};
use nym_validator_client::nyxd::error::NyxdError;
use std::io;
use std::net::SocketAddr;
@@ -33,7 +33,9 @@ pub enum CredentialProxyError {
#[error("the provided expiration date is too early")]
ExpirationDateTooEarly,
#[error("failed to bind to {address}: {source}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?")]
#[error(
"failed to bind to {address}: {source}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?"
)]
SocketBindFailure {
address: SocketAddr,
source: io::Error,
@@ -89,7 +91,7 @@ pub enum CredentialProxyError {
InsufficientNumberOfSigners { available: usize, threshold: u64 },
#[error(
"we have only managed to obtain {available} partial credentials while the minimum threshold is {threshold}"
"we have only managed to obtain {available} partial credentials while the minimum threshold is {threshold}"
)]
InsufficientNumberOfCredentials { available: usize, threshold: u64 },
@@ -102,7 +104,9 @@ pub enum CredentialProxyError {
#[error("the DKG has not yet been initialised in the system")]
UninitialisedDkg,
#[error("credentials can't yet be issued in the system. approximate expected availability: {availability}")]
#[error(
"credentials can't yet be issued in the system. approximate expected availability: {availability}"
)]
CredentialsNotYetIssuable { availability: OffsetDateTime },
#[error("reached seemingly impossible ecash failure")]
@@ -123,6 +127,13 @@ pub enum CredentialProxyError {
#[error("failed to create deposit")]
DepositFailure,
#[error("failed to load jwt signing key from {path}: {err}")]
JWTSigningKeyLoadFailure {
path: String,
#[source]
err: std::io::Error,
},
#[error("can't obtain sufficient number of credential shares due to unavailable quorum")]
UnavailableSigningQuorum,
@@ -140,7 +151,9 @@ pub enum CredentialProxyError {
#[error("failed to obtain wallet shares with id {id}: {message}")]
ShareByIdLoadError { message: String, id: i64 },
#[error("failed to obtain wallet shares with device_id {device_id} and credential_id: {credential_id}: {message}")]
#[error(
"failed to obtain wallet shares with device_id {device_id} and credential_id: {credential_id}: {message}"
)]
ShareByDeviceLoadError {
message: String,
device_id: String,
+1 -1
View File
@@ -1,8 +1,8 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use rand::rngs::OsRng;
use rand::RngCore;
use rand::rngs::OsRng;
use time::OffsetDateTime;
use tracing::{debug, info, warn};
use uuid::Uuid;
+1 -1
View File
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::CredentialProxyError;
use axum::Json;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use nym_credential_proxy_requests::api::v1::ErrorResponse;
use tracing::warn;
use uuid::Uuid;
@@ -5,12 +5,12 @@
// it should have been therefore extracted to a common crate instead and imported as dependency
use crate::error::CredentialProxyError;
use futures::{stream, StreamExt};
use futures::{StreamExt, stream};
use nym_cache::CachedImmutableItems;
use nym_credentials::ecash::utils::{cred_exp_date, ecash_today, EcashTime};
use nym_credentials::ecash::utils::{EcashTime, cred_exp_date, ecash_today};
use nym_validator_client::EcashApiClient;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::dkg_query_client::Epoch;
use nym_validator_client::EcashApiClient;
use std::cmp::min;
use std::future::Future;
use time::{Date, OffsetDateTime};
+104 -3
View File
@@ -5,8 +5,8 @@ use crate::error::CredentialProxyError;
use crate::shared_state::nyxd_client::ChainClient;
use nym_ecash_signer_check::{check_known_dealers, dkg_details_with_client};
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use tokio_util::sync::CancellationToken;
use tracing::{error, info, warn};
@@ -27,6 +27,8 @@ pub struct QuorumStateChecker {
cancellation_token: CancellationToken,
check_interval: Duration,
quorum_state: QuorumState,
max_retries: u32,
retry_initial_delay: Duration,
}
impl QuorumStateChecker {
@@ -42,6 +44,8 @@ impl QuorumStateChecker {
quorum_state: QuorumState {
available: Arc::new(Default::default()),
},
max_retries: 3,
retry_initial_delay: Duration::from_secs(2),
};
// first check MUST succeed, otherwise we shouldn't start
@@ -56,7 +60,102 @@ impl QuorumStateChecker {
self.quorum_state.clone()
}
fn is_retryable_error(&self, err: &CredentialProxyError) -> bool {
let err_str = err.to_string().to_lowercase();
// Check for DNS-related errors
if err_str.contains("dns")
|| err_str.contains("lookup")
|| err_str.contains("name resolution")
|| err_str.contains("temporary failure")
|| err_str.contains("failed to lookup address")
{
return true;
}
// Check if it's a Tendermint RPC error (which could be DNS/timeout related)
if let CredentialProxyError::NyxdFailure { source: nyxd_err } = err {
let nyxd_err_str = nyxd_err.to_string().to_lowercase();
if nyxd_err_str.contains("tendermint rpc request failed") {
return true;
}
if nyxd_err.is_tendermint_response_timeout() {
return true;
}
}
false
}
async fn check_quorum_state(&self) -> Result<bool, CredentialProxyError> {
self.check_quorum_state_with_retry().await
}
async fn check_quorum_state_with_retry(&self) -> Result<bool, CredentialProxyError> {
let mut last_error_msg = None;
let delay = self.retry_initial_delay;
for attempt in 0..=self.max_retries {
match self.check_quorum_state_once().await {
Ok(result) => {
if attempt > 0 {
info!("quorum check succeeded after {} retry attempt(s)", attempt);
}
return Ok(result);
}
Err(err) => {
let err_msg = err.to_string();
// Check if this error is retryable
if !self.is_retryable_error(&err) {
return Err(err);
}
last_error_msg = Some(err_msg.clone());
if attempt >= self.max_retries {
break;
}
// Log the retry attempt
warn!(
"quorum check failed (attempt {}/{}): {}. Retrying in {:?}...",
attempt + 1,
self.max_retries + 1,
err_msg,
delay
);
// Wait before retrying with exponential backoff
tokio::time::sleep(delay).await;
}
}
}
// try one final time to get the actual error
match self.check_quorum_state_once().await {
Ok(result) => {
warn!(
"quorum check succeeded on final attempt after {} retries",
self.max_retries
);
Ok(result)
}
Err(err) => {
if let Some(error_msg) = last_error_msg {
error!(
"quorum check failed after {} retry attempts. Last error: {}",
self.max_retries + 1,
error_msg
);
}
Err(err)
}
}
}
async fn check_quorum_state_once(&self) -> Result<bool, CredentialProxyError> {
let client_guard = self.client.query_chain().await;
// split the operation as we only need to hold the reference to chain client for the first part
@@ -67,7 +166,9 @@ impl QuorumStateChecker {
let res = check_known_dealers(dkg_details).await?;
let Some(signing_threshold) = res.threshold else {
warn!("signing threshold is currently unavailable and we have not yet implemented credential issuance during DKG transition");
warn!(
"signing threshold is currently unavailable and we have not yet implemented credential issuance during DKG transition"
);
return Ok(false);
};
@@ -91,7 +192,7 @@ impl QuorumStateChecker {
break
}
_ = tokio::time::sleep(self.check_interval) => {
match self.check_quorum_state().await {
match self.check_quorum_state_with_retry().await {
Ok(available) => self.quorum_state.available.store(available, Ordering::SeqCst),
Err(err) => error!("failed to check current quorum state: {err}"),
}
@@ -3,7 +3,7 @@
use crate::error::CredentialProxyError;
use crate::nym_api_helpers::{
ensure_sane_expiration_date, query_all_threshold_apis, CachedEpoch, CachedImmutableEpochItem,
CachedEpoch, CachedImmutableEpochItem, ensure_sane_expiration_date, query_all_threshold_apis,
};
use crate::quorum_checker::QuorumState;
use crate::shared_state::nyxd_client::ChainClient;
@@ -16,20 +16,20 @@ use nym_credentials::ecash::utils::EcashTime;
use nym_credentials::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, EpochVerificationKey,
};
use nym_validator_client::EcashApiClient;
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::nyxd::contract_traits::dkg_query_client::Epoch;
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, PagedDkgQueryClient};
use nym_validator_client::nyxd::Coin;
use nym_validator_client::EcashApiClient;
use time::{Date, OffsetDateTime};
use tokio::sync::{RwLock, RwLockReadGuard};
use tracing::info;
pub use nym_compact_ecash::VerificationKeyAuth;
pub use nym_compact_ecash::scheme::coin_indices_signatures::CoinIndexSignatureShare;
pub use nym_compact_ecash::scheme::expiration_date_signatures::ExpirationDateSignatureShare;
pub use nym_compact_ecash::VerificationKeyAuth;
pub use nym_credentials::{IssuanceTicketBook, IssuedTicketBook};
pub use nym_credentials_interface::{TicketType, TicketTypeRepr};
@@ -13,10 +13,10 @@ use nym_credential_proxy_requests::api::v1::ticketbook::models::{
};
use nym_credentials::{AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures};
use nym_ecash_contract_common::deposit::DepositId;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::dkg_query_client::Epoch;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::EcashApiClient;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::nyxd::contract_traits::dkg_query_client::Epoch;
use std::sync::Arc;
use std::time::Duration;
use time::{Date, OffsetDateTime};
@@ -92,7 +92,9 @@ impl CredentialProxyState {
let time_taken = start.elapsed();
let formatted = humantime::format_duration(time_taken);
if time_taken > Duration::from_secs(10) {
warn!("attempting to get buffered deposit took {formatted}. perhaps the buffer is too small or the process/chain is overloaded?")
warn!(
"attempting to get buffered deposit took {formatted}. perhaps the buffer is too small or the process/chain is overloaded?"
)
} else {
debug!("attempting to get buffered deposit took {formatted}")
};
@@ -106,7 +108,9 @@ impl CredentialProxyState {
.insert_deposit_usage_error(deposit_id, error)
.await
{
error!("failed to insert information about deposit (id: {deposit_id}) usage failure: {err}")
error!(
"failed to insert information about deposit (id: {deposit_id}) usage failure: {err}"
)
}
}
@@ -7,7 +7,7 @@ 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, DirectSigningHttpRpcNyxdClient};
use nym_validator_client::{DirectSigningHttpRpcNyxdClient, nyxd};
use std::ops::Deref;
use std::sync::Arc;
use std::time::Duration;
@@ -3,8 +3,8 @@
use crate::error::CredentialProxyError;
use crate::shared_state::nyxd_client::ChainClient;
use nym_validator_client::nyxd::contract_traits::EcashQueryClient;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::nyxd::contract_traits::EcashQueryClient;
use std::sync::Arc;
use time::OffsetDateTime;
use tokio::sync::RwLock;
@@ -380,8 +380,9 @@ impl SqliteStorageManager {
return Ok(());
}
let mut query_builder =
sqlx::QueryBuilder::new("INSERT INTO ecash_deposit (deposit_id, deposit_tx_hash, requested_on, deposit_amount, ed25519_deposit_private_key) ");
let mut query_builder = sqlx::QueryBuilder::new(
"INSERT INTO ecash_deposit (deposit_id, deposit_tx_hash, requested_on, deposit_amount, ed25519_deposit_private_key) ",
);
query_builder.push_values(&deposits, |mut b, deposit| {
b.push_bind(deposit.deposit_id)
+2 -2
View File
@@ -13,8 +13,8 @@ use nym_credentials::{
use nym_validator_client::ecash::BlindedSignatureResponse;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::ecash_query_client::DepositId;
use sqlx::sqlite::{SqliteAutoVacuum, SqliteSynchronous};
use sqlx::ConnectOptions;
use sqlx::sqlite::{SqliteAutoVacuum, SqliteSynchronous};
use std::fmt::Debug;
use std::path::Path;
use std::time::Duration;
@@ -405,8 +405,8 @@ mod tests {
use nym_compact_ecash::scheme::keygen::KeyPairUser;
use nym_crypto::asymmetric::ed25519;
use nym_validator_client::nyxd::{Coin, Hash};
use rand::rngs::OsRng;
use rand::RngCore;
use rand::rngs::OsRng;
use std::ops::Deref;
use tempfile::{NamedTempFile, TempPath};
@@ -4,12 +4,12 @@
use crate::deposits_buffer::DepositsBuffer;
use crate::error::CredentialProxyError;
use crate::quorum_checker::QuorumStateChecker;
use crate::shared_state::CredentialProxyState;
use crate::shared_state::ecash_state::EcashState;
use crate::shared_state::nyxd_client::ChainClient;
use crate::shared_state::required_deposit_cache::RequiredDepositCache;
use crate::shared_state::CredentialProxyState;
use crate::storage::pruner::StoragePruner;
use crate::storage::CredentialProxyStorage;
use crate::storage::pruner::StoragePruner;
use crate::webhook::ZkNymWebhook;
use nym_credentials::ecash::utils::ecash_default_expiration_date;
use nym_validator_client::nym_api::EpochId;
@@ -8,7 +8,7 @@ use nym_credential_proxy_requests::api::v1::ticketbook::models::{
GlobalDataParams, TicketbookWalletSharesResponse,
};
use nym_validator_client::nym_api::EpochId;
use tracing::{debug, span, Instrument, Level};
use tracing::{Instrument, Level, debug, span};
use uuid::Uuid;
impl TicketbookManager {
@@ -7,12 +7,12 @@ use crate::ticketbook_manager::TicketbookManager;
use nym_compact_ecash::Base58;
use nym_credential_proxy_requests::api::v1::ticketbook::models::{
CurrentEpochResponse, DepositResponse, GlobalDataParams, MasterVerificationKeyResponse,
PartialVerificationKey, PartialVerificationKeysResponse, TicketbookAsyncRequest,
TicketbookObtainParams, TicketbookRequest, TicketbookWalletSharesAsyncResponse,
TicketbookWalletSharesResponse,
ObtainTicketBookSharesAsyncResponse, PartialVerificationKey, PartialVerificationKeysResponse,
TicketbookAsyncRequest, TicketbookObtainParams, TicketbookRequest,
TicketbookWalletSharesAsyncResponse, TicketbookWalletSharesResponse,
};
use time::OffsetDateTime;
use tracing::{error, info, span, warn, Instrument, Level};
use tracing::{Instrument, Level, error, info, span, warn};
use uuid::Uuid;
impl TicketbookManager {
@@ -65,7 +65,7 @@ impl TicketbookManager {
uuid: Uuid,
request: TicketbookAsyncRequest,
params: TicketbookObtainParams,
) -> Result<TicketbookWalletSharesAsyncResponse, CredentialProxyError> {
) -> Result<ObtainTicketBookSharesAsyncResponse, CredentialProxyError> {
let requested_on = OffsetDateTime::now_utc();
let span = span!(Level::INFO, "[async] obtain ticketboook", uuid = %uuid);
async move {
@@ -110,7 +110,7 @@ impl TicketbookManager {
}
// 4. in the meantime, return the id to the user
Ok(TicketbookWalletSharesAsyncResponse { id, uuid })
Ok(TicketbookWalletSharesAsyncResponse { id, uuid }.into())
}
.instrument(span)
.await
@@ -4,7 +4,7 @@
use crate::error::CredentialProxyError;
use crate::storage::models::BlindedShares;
use crate::ticketbook_manager::TicketbookManager;
use futures::{stream, StreamExt};
use futures::{StreamExt, stream};
use nym_compact_ecash::Base58;
use nym_credential_proxy_requests::api::v1::ticketbook::models::{
TicketbookAsyncRequest, TicketbookObtainParams, TicketbookRequest,
+1 -1
View File
@@ -3,7 +3,7 @@
use reqwest::header::AUTHORIZATION;
use serde::Serialize;
use tracing::{debug, error, instrument, span, Instrument, Level};
use tracing::{Instrument, Level, debug, error, instrument, span};
use url::Url;
use uuid::Uuid;
@@ -246,7 +246,7 @@ mod tests {
let _exp_date_sigs = generate_expiration_date_signatures(
sig_req.expiration_date.ecash_unix_timestamp(),
&[signing_keys.secret_key()],
&vec![signing_keys.verification_key()],
&[signing_keys.verification_key()],
&signing_keys.verification_key(),
&[1],
)?;
@@ -263,7 +263,7 @@ mod tests {
let wallet = issuance.aggregate_signature_shares(
&signing_keys.verification_key(),
&vec![partial_wallet],
&[partial_wallet],
sig_req,
)?;
@@ -58,7 +58,6 @@ impl PersistentStorage {
"Attempting to connect to database {}",
database_path.as_ref().display()
);
let opts = sqlx::sqlite::SqliteConnectOptions::new()
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
@@ -1,9 +1,9 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::*;
use crate::BandwidthFlushingBehaviourConfig;
use crate::ClientBandwidth;
use crate::error::*;
use nym_credentials::ecash::utils::ecash_today;
use nym_credentials_interface::Bandwidth;
use nym_gateway_requests::ServerResponse;
@@ -1,10 +1,10 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::Error;
use crate::ecash::error::EcashTicketError;
use crate::ecash::helpers::for_each_api_concurrent;
use crate::ecash::state::SharedState;
use crate::Error;
use cosmwasm_std::Fraction;
use cw_utils::ThresholdResponse;
use futures::channel::mpsc::UnboundedReceiver;
@@ -13,22 +13,22 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, 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::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::AccountId;
use nym_validator_client::EcashApiClient;
use si_scale::helpers::bibytes2;
use std::collections::{HashMap, HashSet};
use std::ops::Deref;
use std::sync::atomic::{AtomicUsize, Ordering};
use time::OffsetDateTime;
use tokio::sync::{Mutex, RwLockReadGuard};
use tokio::time::{interval_at, Duration, Instant};
use tokio::time::{Duration, Instant, interval_at};
use tracing::{debug, error, info, instrument, trace, warn};
enum ProposalResult {
@@ -352,7 +352,9 @@ impl CredentialHandler {
Ok(accepted)
}
Err(err) => {
error!("failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later");
error!(
"failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later"
);
Err(EcashTicketError::ApiFailure(EcashApiError::NymApi {
source: nym_validator_client::ValidatorClientError::from(err),
}))
@@ -443,7 +445,9 @@ impl CredentialHandler {
let rejected_ratio = rejected as f32 / total as f32;
let rejected_perc = rejected_ratio * 100.;
if rejected_ratio >= (1. - self.config.minimum_api_quorum) {
error!("{rejected_perc:.2}% of signers rejected ticket {ticket_id}. we won't be able to redeem it");
error!(
"{rejected_perc:.2}% of signers rejected ticket {ticket_id}. we won't be able to redeem it"
);
self.shared_state
.storage
@@ -456,12 +460,19 @@ impl CredentialHandler {
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 ticket {ticket_id}. we won't be able to spend it"),
n if n < self.config.minimum_api_quorum => warn!("less than 80%, but more than 67% of signers ({accepted_perc:.2}%) accepted ticket {ticket_id}. technically we could redeem it, but we'll wait for the bigger quorum"),
n if n < self.multisig_threshold => error!(
"less than 2/3 of signers ({accepted_perc:.2}%) accepted ticket {ticket_id}. we won't be able to spend it"
),
n if n < self.config.minimum_api_quorum => warn!(
"less than 80%, but more than 67% of signers ({accepted_perc:.2}%) accepted ticket {ticket_id}. technically we could redeem it, but we'll wait for the bigger quorum"
),
_ => {
trace!("{accepted_perc:.2}% of signers accepted ticket {ticket_id}");
self.shared_state.storage.update_verified_ticket(pending.ticket.ticket_id).await?;
return Ok(true)
self.shared_state
.storage
.update_verified_ticket(pending.ticket.ticket_id)
.await?;
return Ok(true);
}
}
@@ -484,7 +495,10 @@ impl CredentialHandler {
.send_pending_ticket_for_verification(&mut pending, Some(api_clients))
.await?;
if !got_quorum {
debug!("failed to reach quorum for ticket {}. apis: {:?} haven't responded. we'll retry later", pending.ticket.ticket_id, pending.pending);
debug!(
"failed to reach quorum for ticket {}. apis: {:?} haven't responded. we'll retry later",
pending.ticket.ticket_id, pending.pending
);
self.pending_tickets.push(pending);
} else {
// since we reached the quorum we no longer need to hold the ticket's binary data
@@ -513,7 +527,10 @@ impl CredentialHandler {
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);
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
@@ -527,7 +544,9 @@ impl CredentialHandler {
}
}
Err(err) => {
error!("experienced internal error when attempting to resolve pending proposal: {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);
@@ -547,7 +566,10 @@ impl CredentialHandler {
{
Ok(got_quorum) => {
if !got_quorum {
warn!("still failed to reach quorum for ticket {}. apis: {:?} haven't responded. we'll retry later", pending.ticket.ticket_id, pending.pending);
warn!(
"still failed to reach quorum for ticket {}. apis: {:?} haven't responded. we'll retry later",
pending.ticket.ticket_id, pending.pending
);
still_failing.push(pending);
} else {
// since we reached the quorum we no longer need to hold the ticket's binary data
@@ -558,7 +580,9 @@ impl CredentialHandler {
}
}
Err(err) => {
error!("experienced internal error when attempting to resolve pending ticket: {err}");
error!(
"experienced internal error when attempting to resolve pending ticket: {err}"
);
// make sure to update internal state to not lose any data
self.pending_tickets.push(pending);
self.pending_tickets.append(&mut still_failing);
@@ -591,7 +615,9 @@ impl CredentialHandler {
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");
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)
}
}
@@ -713,7 +739,9 @@ impl CredentialHandler {
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");
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);
}
@@ -722,11 +750,15 @@ impl CredentialHandler {
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");
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}");
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}");
@@ -784,7 +816,9 @@ impl CredentialHandler {
None
}
(_, Some(on_chain)) => {
warn!("we seem to have crashed after creating proposal, but before persisting it onto disk!");
warn!(
"we seem to have crashed after creating proposal, but before persisting it onto disk!"
);
Some(on_chain)
}
@@ -805,12 +839,20 @@ impl CredentialHandler {
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);
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);
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(());
}
}
@@ -879,7 +921,10 @@ impl CredentialHandler {
.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);
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
@@ -896,7 +941,9 @@ impl CredentialHandler {
}
async fn periodic_operations(&mut self) -> Result<(), EcashTicketError> {
trace!("attempting to resolve all pending operations -> tickets that are waiting for verification and possibly redemption");
trace!(
"attempting to resolve all pending operations -> tickets that are waiting for verification and possibly redemption"
);
// 1. retry all operations that have failed in the past: verification requests and pending redemption
self.resolve_pending().await?;
@@ -72,7 +72,9 @@ pub enum EcashTicketError {
#[error("the DKG contract is unavailable")]
UnavailableDkgContract,
#[error("the DKG threshold value for epoch {epoch_id} is currently unavailable. we're probably mid-epoch transition")]
#[error(
"the DKG threshold value for epoch {epoch_id} is currently unavailable. we're probably mid-epoch transition"
)]
DKGThresholdUnavailable { epoch_id: EpochId },
#[error("could not create redemption proposal as we have tickets pending full verification")]
@@ -9,10 +9,10 @@ use error::EcashTicketError;
use futures::channel::mpsc::{self, UnboundedSender};
use nym_credentials::CredentialSpendingData;
use nym_credentials_interface::{ClientTicket, CompactEcashError, NymPayInfo, VerificationKeyAuth};
use nym_gateway_storage::traits::BandwidthGatewayStorage;
use nym_gateway_storage::GatewayStorage;
use nym_validator_client::nym_api::EpochId;
use nym_gateway_storage::traits::BandwidthGatewayStorage;
use nym_validator_client::DirectSigningHttpRpcNyxdClient;
use nym_validator_client::nym_api::EpochId;
use state::SharedState;
use time::OffsetDateTime;
use tokio::sync::{Mutex, RwLockReadGuard};
@@ -1,19 +1,19 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ecash::error::EcashTicketError;
use crate::Error;
use cosmwasm_std::{from_json, CosmosMsg, WasmMsg};
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::AccountId;
use nym_validator_client::{DirectSigningHttpRpcNyxdClient, EcashApiClient};
use std::collections::BTreeMap;
use std::ops::Deref;
@@ -53,7 +53,9 @@ impl SharedState {
}
let Ok(current_epoch) = nyxd_client.get_current_epoch().await else {
error!("the specified DKG contract address is invalid - no coconut credentials will be redeemable");
error!(
"the specified DKG contract address is invalid - no coconut credentials will be redeemable"
);
// if we require coconut credentials, we MUST have DKG contract available
return Err(EcashTicketError::UnavailableDkgContract.into());
};
+3 -1
View File
@@ -35,7 +35,9 @@ pub enum Error {
#[error("This gateway is only accepting coconut credentials for bandwidth")]
OnlyCoconutCredentials,
#[error("insufficient bandwidth available to process the request. required: {required}B, available: {available}B")]
#[error(
"insufficient bandwidth available to process the request. required: {required}B, available: {available}B"
)]
OutOfBandwidth { required: i64, available: i64 },
#[error("Internal gateway storage error")]
+1 -1
View File
@@ -4,7 +4,7 @@
use crate::ecash::traits::EcashManager;
use async_trait::async_trait;
use bandwidth_storage_manager::BandwidthStorageManager;
use nym_credentials::ecash::utils::{cred_exp_date, ecash_today, EcashTime};
use nym_credentials::ecash::utils::{EcashTime, cred_exp_date, ecash_today};
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
use nym_gateway_requests::models::CredentialSpendingRequest;
use std::sync::Arc;
+5 -4
View File
@@ -7,9 +7,12 @@ use thiserror::Error;
use time::{Date, OffsetDateTime};
pub use nym_compact_ecash::{
Base58, BlindedSignature, Bytable, EncodedDate, EncodedTicketType, PartialWallet, PayInfo,
PublicKeyUser, SecretKeyUser, VerificationKeyAuth, WithdrawalRequest,
aggregate_verification_keys, aggregate_wallets, constants, ecash_parameters,
error::CompactEcashError,
generate_keypair_user, generate_keypair_user_from_seed, issue_verify,
scheme::Payment,
scheme::coin_indices_signatures::aggregate_indices_signatures,
scheme::coin_indices_signatures::{
AnnotatedCoinIndexSignature, CoinIndexSignature, CoinIndexSignatureShare,
@@ -22,12 +25,10 @@ pub use nym_compact_ecash::{
},
scheme::keygen::KeyPairUser,
scheme::withdrawal::RequestInfo,
scheme::Payment,
scheme::{Wallet, WalletSignatures},
withdrawal_request, Base58, BlindedSignature, Bytable, EncodedDate, EncodedTicketType,
PartialWallet, PayInfo, PublicKeyUser, SecretKeyUser, VerificationKeyAuth, WithdrawalRequest,
withdrawal_request,
};
pub use nym_ecash_time::{ecash_today, EcashTime};
pub use nym_ecash_time::{EcashTime, ecash_today};
pub use nym_network_defaults::TicketTypeRepr;
#[derive(Debug, Clone)]

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