Compare commits

...

226 Commits

Author SHA1 Message Date
benedetta davico a98306d340 Update Cargo.toml 2026-01-26 18:26:43 +01:00
benedettadavico b68e13f0f2 update changelog 2026-01-13 16:47:13 +01:00
Jędrzej Stuczyński 46fe1bc819 bugfix: mozzarella -> niolo config migration (#6259)
* bugfix: mozzarella -> niolo config migration

* clippy
2025-12-02 15:29:30 +00:00
benedettadavico 37ae72d8ec bump versions 2025-11-28 19:18:05 +01:00
Jędrzej Stuczyński 0b58b6f728 remove run DKG migration (#6253) 2025-11-28 13:16:36 +00:00
benedetta davico e709e30e43 Merge pull request #6144 from nymtech/ns/weighted-scoring
Add weighted scoring to NS API
2025-11-28 02:05:44 -08:00
benedetta davico 29b405f813 Merge pull request #6236 from nymtech/master
sync master to develop
2025-11-27 03:23:28 -08:00
Jędrzej Stuczyński f5d22a66f6 bugfix: reexposed 'derive_extended_private_key' (#6247) 2025-11-27 10:28:53 +00:00
import this bf7cd15428 Merge pull request #6235 from nymtech/serinko/release-notes/v2025.21-mozzarella
[DOCs/operators]: Release notes for `v2025.21-mozzarella`
2025-11-26 13:58:40 +00:00
benedetta davico 757da6a456 Merge pull request #6234 from nymtech/release/2025.21-mozzarella
Release/2025.21 mozzarella
2025-11-26 04:52:46 -08:00
benedetta davico 62c581a9ae Merge pull request #6233 from nymtech/release/2025.21-mozzarella
Release/2025.21 mozzarella
2025-11-26 04:52:27 -08:00
serinko 32e06e19e7 bump stats 2025-11-26 13:18:47 +01:00
serinko fd1b524037 add operators news 2025-11-26 13:13:37 +01:00
serinko 36d0adfe92 add NTM info message 2025-11-26 12:15:50 +01:00
serinko fcf782674c bump up version 2025-11-26 12:08:27 +01:00
serinko 4b35c36299 add dev notes 2025-11-26 12:05:48 +01:00
Jędrzej Stuczyński ca7cbac320 chore: don't rederive wallet keys on every tx (#6213)
* chore: make 'DirectSecp256k1HdWallet' only derive its keys once on construction

Previously all the keys and account information was being derived for every transaction signed

* no longer keep account seed on the wallet struct
2025-11-26 10:45:01 +00:00
dependabot[bot] e410aecf40 build(deps): bump tower-http from 0.5.2 to 0.6.6 (#6030)
Bumps [tower-http](https://github.com/tower-rs/tower-http) from 0.5.2 to 0.6.6.
- [Release notes](https://github.com/tower-rs/tower-http/releases)
- [Commits](https://github.com/tower-rs/tower-http/compare/tower-http-0.5.2...tower-http-0.6.6)

---
updated-dependencies:
- dependency-name: tower-http
  dependency-version: 0.6.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:43:33 +00:00
dependabot[bot] ae76335c31 build(deps): bump tracing-subscriber from 0.3.19 to 0.3.20 (#5993)
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.19 to 0.3.20.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-version: 0.3.20
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:26:12 +00:00
dependabot[bot] dac7f1f83c build(deps): bump tracing-subscriber in /nym-wallet (#5994)
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.19 to 0.3.20.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-version: 0.3.20
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:25:52 +00:00
dependabot[bot] 31ff3645c5 build(deps): bump actions/upload-pages-artifact from 3 to 4 (#5992)
Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:22:50 +00:00
dependabot[bot] 6dd3b78a74 build(deps): bump actions/setup-go from 5 to 6 (#6013)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:21:57 +00:00
dependabot[bot] 0ab14f7041 build(deps): bump ammonia from 4.1.1 to 4.1.2 (#6057)
Bumps [ammonia](https://github.com/rust-ammonia/ammonia) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/rust-ammonia/ammonia/releases)
- [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.1...v4.1.2)

---
updated-dependencies:
- dependency-name: ammonia
  dependency-version: 4.1.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:21:30 +00:00
dependabot[bot] 9c6310264e build(deps): bump tar-fs (#6063)
Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 3.0.9 to 3.1.1.
- [Commits](https://github.com/mafintosh/tar-fs/compare/v3.0.9...v3.1.1)

---
updated-dependencies:
- dependency-name: tar-fs
  dependency-version: 3.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:21:06 +00:00
dependabot[bot] aa37bfb7ff build(deps): bump mikefarah/yq from 4.47.1 to 4.48.1 (#6107)
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.47.1 to 4.48.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.47.1...v4.48.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-version: 4.48.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:20:42 +00:00
dependabot[bot] 66c2454775 Bump min-document from 2.19.0 to 2.19.1 (#6181)
Bumps [min-document](https://github.com/Raynos/min-document) from 2.19.0 to 2.19.1.
- [Commits](https://github.com/Raynos/min-document/compare/v2.19.0...v2.19.1)

---
updated-dependencies:
- dependency-name: min-document
  dependency-version: 2.19.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:20:22 +00:00
dependabot[bot] 670f383faa Bump js-yaml in /sdk/typescript/tests/integration-tests/mix-fetch (#6215)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:20:05 +00:00
dependabot[bot] 9714351fd8 Bump glob from 10.3.4 to 10.5.0 in /documentation/scripts/post-process (#6216)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.4 to 10.5.0.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.4...v10.5.0)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 10.5.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:19:48 +00:00
dependabot[bot] 7352499328 Bump golang.org/x/crypto in /nym-gateway-probe/netstack_ping (#6220)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.39.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.39.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:19:30 +00:00
dependabot[bot] 06717037e5 Bump js-yaml in /sdk/typescript/codegen/contract-clients (#6231)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.14.1 to 3.14.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-26 10:18:41 +00:00
Tommy Verrall 1a7aa2ce90 Merge pull request #6068 from nymtech/dependabot/github_actions/dot-github/workflows/SonarSource/sonarqube-scan-action-6
build(deps): bump SonarSource/sonarqube-scan-action from 5 to 6 in /.github/workflows
2025-11-26 01:06:33 -08:00
Tommy Verrall c594361cd1 Merge pull request #6180 from nymtech/dependabot/npm_and_yarn/nym-node-status-api/nym-node-status-ui/next-15.4.7
Bump next from 15.4.1 to 15.4.7 in /nym-node-status-api/nym-node-status-ui
2025-11-26 01:05:53 -08:00
Tommy Verrall df52f10f52 Merge pull request #6219 from PaJaSoft/chain-link-fix
Update chain registry link
2025-11-26 00:41:55 -08:00
benedetta davico 6a96d8205b Bump version from 4.0.11-testing to 4.0.12 2025-11-26 09:33:17 +01:00
benedettadavico 22793bc45e update changelog 2025-11-25 15:16:42 +01:00
Simon Wicky 6eb8f29235 Statistics API v2 (#6227)
* vpn client report v2

* report v2 support in nym-stats API

* version bump

* CI fix while we're at it

* more CI fix

* needed the dind after all

* PR comments
2025-11-25 13:16:31 +01:00
import this c9ef46c51d Merge pull request #6230 from nymtech/seriniko/nym-node-cli/cmd-output
[DOCs/operators]: Update nym-node-cli guide
2025-11-25 11:39:28 +00:00
serinko f1024bc976 improve formatting 2025-11-25 11:13:46 +01:00
serinko 1aec4c2f8e fix typo 2025-11-25 11:11:16 +01:00
serinko 1b79107726 update explorer part 2025-11-25 11:02:26 +01:00
serinko a69b473ba1 update explorer part 2025-11-25 11:00:14 +01:00
serinko fe01c922c0 improve formatting 2025-11-25 10:52:58 +01:00
serinko 4b0fbc663a improve formatting 2025-11-25 10:51:41 +01:00
serinko 4b292ca142 update nym-node-cli guide 2025-11-25 10:02:28 +01:00
import this 9262e24892 Merge pull request #6186 from nymtech/operators/tools-rewamp
Operator tools rewamp
2025-11-24 14:48:41 +00:00
serinko e0c74c5eb0 formatting fix ... LFG 2025-11-24 15:33:51 +01:00
serinko a293d6da7d full_tunnel_setup to nym_tunnel_setup 2025-11-24 15:20:45 +01:00
RadekSabacky f12a554e85 Merge remote-tracking branch 'origin/operators/tools-rewamp' into operators/tools-rewamp 2025-11-24 15:11:20 +01:00
RadekSabacky 8c3a797750 @ fix perform_pings 2025-11-24 15:11:01 +01:00
serinko 00d0ae0b5b docs: add noninteractive mode for quic setup 2025-11-24 14:35:00 +01:00
serinko de0ae687ef docs: specify command desc 2025-11-24 14:24:04 +01:00
benedettadavico 42c051dfa3 add default output test 2025-11-24 13:44:28 +01:00
benedettadavico 26f4dd8f39 add another test 2025-11-24 13:41:03 +01:00
serinko 2d37c33a3d tweak docs commands 2025-11-24 12:44:04 +01:00
RadekSabacky f1be6ae788 @ rename $cmd -> item in exit_policy_install_deps 2025-11-24 12:38:50 +01:00
serinko c13b4aa745 fix coloring and trap 2025-11-24 12:25:59 +01:00
serinko 68eae18b8b fix coloring and trap 2025-11-24 12:21:03 +01:00
serinko 28dc7cae4d add logging and logfile 2025-11-24 11:40:22 +01:00
Simon Wicky 37f3ef58a3 [bugfix] Tunnel not waiting on MixnetClient to shut down cleanly (#6225)
* return the handlefor a clean shutdown

* cargo lock
2025-11-21 16:39:12 +01:00
serinko 52f98de73b simplify 2025-11-21 12:40:24 +01:00
serinko 6d63ba1f4c menu fix 2025-11-21 12:17:50 +01:00
import this 6170ca2a14 Update time-now.md 2025-11-21 11:04:48 +00:00
import this 2cc59aadc5 Merge branch 'develop' into operators/tools-rewamp 2025-11-21 11:02:35 +00:00
import this 89dc865ec6 Merge pull request #6217 from nymtech/radek/network_scripts_edit
Radek review and rework of the Network Tunnel Manager tool
2025-11-21 10:57:20 +00:00
import this 6d1d9d58a5 Merge branch 'operators/tools-rewamp' into radek/network_scripts_edit 2025-11-21 10:48:04 +00:00
benedetta davico 3057721845 Merge pull request #6212 from nymtech/probe-fixes
gateway-probe fixes for run-local
2025-11-20 18:07:47 +01:00
benedettadavico 54c7a01482 update version 2025-11-20 17:36:30 +01:00
RadekSabacky 76993a9b94 / colors 2025-11-20 17:36:27 +01:00
benedettadavico b00e1f2fff addressing comment 2025-11-20 17:32:57 +01:00
RadekSabacky ef7974fde9 / otpimize create_nym_chain 2025-11-20 17:29:42 +01:00
RadekSabacky a488a1b489 / color fixes 2025-11-20 16:50:05 +01:00
RadekSabacky bb5b43492a + colors show_exit_policy_status 2025-11-20 16:28:05 +01:00
RadekSabacky f9e2311574 end status of help 2025-11-20 16:21:34 +01:00
RadekSabacky 50d768976f end status of help 2025-11-20 16:20:15 +01:00
RadekSabacky 3825d5f173 Merge branch 'local/radek_benny_merge' into radek/network_scripts_edit 2025-11-20 16:06:05 +01:00
RadekSabacky 9b076197b1 / refactor help section 2025-11-20 16:05:22 +01:00
RadekSabacky 7b96adf7a8 / refactor help section 2025-11-20 16:04:22 +01:00
RadekSabacky 18d271f481 + colors test_exit_policy_connectivity 2025-11-20 15:47:59 +01:00
RadekSabacky 752c7915b3 + colors for check the firewall setup 2025-11-20 14:47:41 +01:00
RadekSabacky 6c01c9fceb Merge 'origin/fixing-order' 2025-11-20 14:41:45 +01:00
RadekSabacky 4e7b4715b0 / test failed echo text 2025-11-20 14:37:46 +01:00
import this 4fdbcb051a Merge pull request #6218 from nymtech/docs/tools-rewamp - [DOCs/operators]: Tools rewamp documentation 2025-11-20 13:03:48 +00:00
serinko 47c6006bb7 ready to merge back 2025-11-20 13:52:41 +01:00
serinko dcfd0f77ad debug trace ticks 2025-11-20 13:39:11 +01:00
serinko 78fb779010 write wg exit policy testing steps 2025-11-20 13:33:06 +01:00
serinko b4544c2b48 wg exit policy setup 2025-11-20 13:17:40 +01:00
benedettadavico 90e07d9980 weighted scoring and unit test 2025-11-20 12:49:11 +01:00
PaJaSoft b8479e1cde Update chain registry link 2025-11-20 01:24:16 +01:00
serinko 37e3a101b1 fix routing test 2025-11-19 17:41:35 +01:00
serinko 45a1074377 remove redundant 2025-11-19 17:35:33 +01:00
serinko 1b9af19e20 update routing configuration steps and make components 2025-11-19 17:14:44 +01:00
RadekSabacky 7a339d4c4d + color everywhere 2025-11-19 16:35:40 +01:00
RadekSabacky 568268d39b + color exit_policy_run_tests 2025-11-19 14:32:33 +01:00
RadekSabacky 9c5847dc67 @ fix failing exit_policy_run_tests 2025-11-19 14:26:16 +01:00
RadekSabacky 22db132c09 @ merge fix test_default_reject_rule 2025-11-19 14:14:19 +01:00
RadekSabacky 5496cce5c9 / move color definition 2025-11-19 13:48:36 +01:00
RadekSabacky 8de37eb6c9 / move ensure_jq where needed 2025-11-19 13:43:24 +01:00
RadekSabacky 40a7a87c61 + colorl jq install 2025-11-19 13:36:41 +01:00
RadekSabacky 95ee3a7c7d + colors test_forward_chain_hook & complete_networking_configuration 2025-11-19 13:34:35 +01:00
RadekSabacky 4736f1eb52 / fix login in exit_policy_run_tests 2025-11-19 13:26:14 +01:00
RadekSabacky c23e139eaa + COLORS test_default_reject_rule 2025-11-19 13:11:05 +01:00
RadekSabacky bfcb4c79bb / fix test_default_reject_rule 2025-11-19 13:07:08 +01:00
RadekSabacky 2933732225 Revert "+ add output for no rules were deduplicated"
This reverts commit 06c0c36c67.
2025-11-18 17:08:51 +01:00
RadekSabacky 2e059865a1 Revert "/ move ensure_jq where needed"
This reverts commit bdc0f5022d.
2025-11-18 17:08:12 +01:00
RadekSabacky 06c0c36c67 + add output for no rules were deduplicated 2025-11-18 17:04:24 +01:00
RadekSabacky bdc0f5022d / move ensure_jq where needed 2025-11-18 16:38:01 +01:00
benedettadavico b742ace7e0 add firewall check to the main script 2025-11-18 14:24:19 +01:00
benedettadavico 82a9563ca0 add a checker script 2025-11-18 13:12:35 +01:00
benedettadavico ef25480c20 fix 2025-11-17 17:05:34 +01:00
benedettadavico bcce854a8b more attempts 2025-11-17 16:03:28 +01:00
benedettadavico f960bfa91b probe fixes
testing probe locally
2025-11-17 11:40:27 +01:00
Mark Sinclair 96e3ff2af9 Node Status UI (#6210)
Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-11-17 09:18:01 +00:00
serinko 4f991061dd fix nginx errors 2025-11-14 16:22:58 +01:00
Jędrzej Stuczyński d73b7b7127 chore: remove support for legacy mixnode within the performance contract (#6205) 2025-11-14 15:04:59 +00:00
Jędrzej Stuczyński 440aadf124 chore: updated default endpoint for retrieving attestation.json (#6207) 2025-11-14 15:04:51 +00:00
serinko e5aef76256 non-interactive 2025-11-14 15:27:44 +01:00
serinko 6acc54d2bc syntax fix 2025-11-14 15:03:36 +01:00
Jędrzej Stuczyński d126d8e5a0 feat: upgrade mode: VPN adjustments (#6189)
* placeholder handling of wg registration with upgrade mode token

* include upgrade mode credentials as part of credential storage

* introduce helper for decoding JWT payload

* expose methods for removing emergency credentials from the storage

* don't allow duplicate emergency credentials with the same content

* added authenticator ClientMessage for upgrade mode check

* retrieve credentials with longest expiration first

* post rebasing fixes

* fixed gateway config

* feat: allow specifying minimum node performance for client init

* nym-node UM improvements

* fixed upgrade mode bandwidth on initial authentication

* fix: logs and thresholds

* expose attestation information from nym-node http api

* additional logs

* post rebasing fixes

* make @simonwicky happy by removing empty lines in emergency_credential table definition

* chore: remove '_' prefix for internal counters within in-mem ecash storage

* improved import of 'UpgradeModeState' within the nym-node

* use explicit time dependency within credential-storage

* re-order imports within the gateway-client

* moved 'AvailableBandwidth' definition to the monorepo
2025-11-14 13:34:36 +00:00
serinko ab6e08dd13 fix logic of landing-page lookup 2025-11-14 14:27:38 +01:00
serinko e09066858c bump up version 2025-11-14 14:21:50 +01:00
Jędrzej Stuczyński 6b2bb3029b feat: merge intermediate upgrade mode changes (#6174)
* squashing feat: merge intermediate upgrade mode changes #6174 to more easily resolve merge conflicts during rebasing

added additional v2 query for metadata endpoint for requesting upgrade mode recheck

added additional message to v6 authenticator to request explicit upgrade mode recheck

clippy

test fixes due to updated keys

updated assertion for upgrading v1 top up request to v2

compare attester public key against the expected value within the credential proxy

use pre-generated attestation public keys within nym-nodes

remove version deprecation

bugfix: default bandwidth response for authenticator

expose upgrade mode information in authenticator responses

adding tests for new v2 server

passing upgrade mode information in metadata endpoint

v2 wireguard private metadata

bugfix: make sure to immediately poll for attestation after spawning task

fix gateway probe and remove code duplication for finalizing registration

squashing before rebasing

post rebasing fixes

AuthenticatorVersion helpers

additional nits

allow unwraps in mocks

fixed linux build

clippy

integrating upgrade mode into authenticator

fixed build after adding wrappers to response types

conditionally updating peer handle bandwidth

cleanup

negotiate initial protocol during registration

change auth to use highest protocol

handler for JWT message

dont meter client bandwidth in upgrade mode

handling recheck requests

sending information about upgrade_mode on client messages

gateway watching for upgrade mode attestation

wip: gateways to disable bandwidth metering on upgrade mode

* fixed ServerResponse deserialisation

* fixed incorrect swagger path for upgrade mode check endpoint

* moved upgrade mode endpoint out of bandwidth routes

* chore: remove unused error variant

* removed re-export of UpgradeModeAttestation from credentials-interface

* chore: define single source of truth for minimum bandwidth threshold value

* moved type definitions out of traits.rs

* updated v6 versioning to point to niolo release instead

* fixed incorrect error mapping
2025-11-14 13:13:15 +00:00
RadekSabacky 842ce93a60 remove duplicate ufw rule 2025-11-14 13:24:20 +01:00
RadekSabacky ce26105986 typo 2025-11-14 13:24:05 +01:00
serinko cc95358385 add email to a fallback 2025-11-14 13:08:05 +01:00
serinko cc04a09ed7 remove redundant work 2025-11-14 12:58:03 +01:00
serinko ae47d53f0c enforce root 2025-11-14 12:49:10 +01:00
serinko e0ff09f323 enforce root 2025-11-14 12:44:57 +01:00
serinko 10707fd2c5 convention Y/n 2025-11-14 12:43:54 +01:00
serinko 9bdd2af14c enforce root 2025-11-14 12:42:05 +01:00
serinko 228ef8b158 add else 2025-11-14 12:40:14 +01:00
import this d820131d2c arg consistency
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 10:36:45 +00:00
import this 054715a600 robust error handling
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 10:36:23 +00:00
import this 3f560180b7 remove redundant
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 10:35:42 +00:00
import this f62dbbdae0 ensure idempotency for the iptable rules
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 10:33:23 +00:00
import this edecc4ba01 remove redundant detect interface
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 10:31:26 +00:00
benedettadavico 1a4d64a0e5 bump versions 2025-11-14 11:23:47 +01:00
benedetta davico 4dcc568ec2 Merge pull request #6204 from nymtech/master
merging master to develop to maintain sync
2025-11-14 11:21:13 +01:00
benedetta davico 468835e3a2 Merge pull request #6199 from nymtech/release/2025.20-leerdammer
Release/2025.20 leerdammer
2025-11-14 10:36:23 +01:00
benedetta davico 28a866e26d Merge pull request #6198 from nymtech/release/2025.20-leerdammer
Release/2025.20 leerdammer
2025-11-14 10:36:11 +01:00
Jędrzej Stuczyński 350d244032 bugfix: fix credential proxy upgrade mode attestation url arg (#6202)
this includes bringing over changes introduced in #6174
2025-11-14 08:19:21 +00:00
Jack Wampler 17ca000782 HTTP API resilience enable & domain rotation conditions (#6200)
* http url fallback conditions

* include changes and tests for fronted

* Allow for explicit DNS error Handling in HTTP client  (#6201)

when sending http reqs add manual DNS so we can handle errors directly

* Address PR nits

---------

Co-authored-by: durch <durch@users.noreply.github.com>
2025-11-14 08:59:36 +01:00
serinko 58c0e289c2 syntax fix 2025-11-13 20:56:04 +01:00
serinko 6d8edc4bc7 replace y to Y and '' 2025-11-13 20:46:33 +01:00
serinko a44cdf1c7c flush nginx script anew 2025-11-13 20:37:35 +01:00
serinko 6b8a6283a4 fix nginx script 2025-11-13 20:27:43 +01:00
import this 94151965bb string to dict fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 17:55:09 +00:00
import this e8ca490db1 style
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 17:54:42 +00:00
import this fe7470ea44 address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 17:54:17 +00:00
serinko 21d52244cb sync up with new tunnel manager 2025-11-13 18:49:53 +01:00
import this c0c58026a8 Merge pull request #6197 from nymtech/serinko/ip-tables-rewamp
one ring rules over all
2025-11-13 17:27:56 +00:00
import this 0fe863c889 delete to resolve merge conflict 2025-11-13 17:27:04 +00:00
import this 4e5d88f64c deleting to resolve merge confilict 2025-11-13 17:26:20 +00:00
serinko 1559f6a912 bugfix 2025-11-13 17:55:57 +01:00
serinko 766024be27 break into args 2025-11-13 17:51:39 +01:00
serinko 5ba181b118 break into args 2025-11-13 17:47:58 +01:00
import this 76fc9f4a90 syntax fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:23:38 +00:00
import this 8ca6af7c86 syntax fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:23:18 +00:00
import this 45e14a7fb1 address comments
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:22:57 +00:00
import this a38917cb9b address comments
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:22:34 +00:00
serinko cf8a399089 remove subshell 2025-11-13 17:07:20 +01:00
import this ba01820586 address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:05:10 +00:00
import this 8c799b2976 address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:04:34 +00:00
import this de4fb6291c address comments
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:03:50 +00:00
import this 81fd37e5c0 address comments
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 16:02:56 +00:00
serinko 219f3af967 remove subshell 2025-11-13 16:57:34 +01:00
import this aea7442525 add status
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:56:52 +00:00
import this 1525aed657 expand pattern to common naming conventions
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:56:18 +00:00
import this 943b5fa8bc address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:51:01 +00:00
import this 71e0c025c6 address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:50:42 +00:00
import this 239c6c701b address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:50:19 +00:00
import this 91d0b7bdad address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:49:49 +00:00
import this 99b28b2b6f address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:49:32 +00:00
import this 5627ada57e address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:49:02 +00:00
serinko 4e1228fff0 ensure cars passing in a shell 2025-11-13 16:43:39 +01:00
serinko 04be5624fa ensure cars passing in a shell 2025-11-13 16:41:42 +01:00
serinko a6fe1b1de7 fix logic to ensure to more robust 2025-11-13 16:32:40 +01:00
import this c5971d0e9d align space
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:18:54 +00:00
import this 06dd74ba34 address comments
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:18:38 +00:00
serinko c6a0256b03 remove wrong stdout 2025-11-13 16:13:25 +01:00
import this d04b61a88b spacing
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:10:01 +00:00
import this 70a119ac58 address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:09:38 +00:00
import this e2fe3a60a6 address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 15:01:23 +00:00
serinko 71301ee0cc sync ipv4 w ipv6 2025-11-13 15:08:34 +01:00
serinko aba6c9d4ac fix exit message 2025-11-13 15:04:17 +01:00
serinko c617bbb240 fix jq 2025-11-13 14:59:36 +01:00
serinko 1f8144eb11 add a safeguard 2025-11-13 14:47:02 +01:00
import this 694135c81b address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 13:38:45 +00:00
import this e815f08505 address comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 13:38:15 +00:00
serinko f402da8e60 add new top manager tool 2025-11-13 14:28:38 +01:00
serinko 34a500d0a2 refactor completely 2025-11-13 14:26:49 +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
serinko ef52f25564 address comment 2025-11-13 11:20:13 +01:00
import this 010b013094 comment fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 10:17:04 +00:00
benedettadavico c503a5f0e8 few more tweaks 2025-11-13 10:21:37 +01:00
serinko 781afd3522 add arg 2025-11-12 17:36:29 +01:00
serinko 58083df0b0 fix QUIC helper script 2025-11-12 17:36:29 +01:00
benedettadavico 4e8d29d0c5 Merge remote-tracking branch 'origin/operators/tools-rewamp' into operators/tools-rewamp 2025-11-12 17:19:32 +01:00
benedettadavico 66797efa80 test new order of events.. 2025-11-12 17:18:59 +01:00
serinko 5a26fa262e add uplink override arg 2025-11-12 16:58:10 +01:00
serinko 73a34935ae trims 2025-11-12 16:29:52 +01:00
mfahampshire fa40acbeca fixed broken link (#6193) 2025-11-12 15:12:38 +00:00
serinko a8086675d9 metadata port inside nymwg 2025-11-12 15:34:00 +01:00
serinko 0453345d65 address comments 2025-11-12 15:32:41 +01:00
serinko b56d9505e6 address comments 2025-11-12 15:31:08 +01:00
serinko bdacc72003 rm redundant 2025-11-12 15:22:26 +01:00
benedettadavico 9eca9efd82 fix direction and add test 2025-11-12 13:45:33 +01: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
benedettadavico babf113fe5 update changelog 2025-11-12 08:39:48 +01:00
serinko cc74d218fc rm redundant fn 2025-11-11 21:56:15 +01:00
serinko dea8a287e6 add arguments for env vars 2025-11-11 19:42:03 +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
serinko fc5d310935 add QUIC setup script to nym-node-cli 2025-11-11 15:04:09 +01: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
dependabot[bot] de27778192 Bump next in /nym-node-status-api/nym-node-status-ui
Bumps [next](https://github.com/vercel/next.js) from 15.4.1 to 15.4.7.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.4.1...v15.4.7)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.4.7
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-07 21:19:35 +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
benedettadavico 0d7487f530 bump versions 2025-10-31 13:24:27 +01:00
dependabot[bot] 37c4a3aa36 build(deps): bump SonarSource/sonarqube-scan-action
Bumps [SonarSource/sonarqube-scan-action](https://github.com/sonarsource/sonarqube-scan-action) from 5 to 6.
- [Release notes](https://github.com/sonarsource/sonarqube-scan-action/releases)
- [Commits](https://github.com/sonarsource/sonarqube-scan-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: SonarSource/sonarqube-scan-action
  dependency-version: '6'
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-26 13:13:05 +00:00
428 changed files with 22501 additions and 9152 deletions
@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v4
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -10,13 +10,13 @@ env:
jobs:
check-if-tag-exists:
runs-on: arc-ubuntu-22.04-dind
runs-on: arc-linux-latest-dind
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup yarn
run: npm install -g yarn
@@ -54,6 +54,6 @@ jobs:
- name: Lint
run: yarn lint
- name: Typecheck with tsc
run: yarn tsc
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
components: rustfmt, clippy
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "1.24.6"
+1 -1
View File
@@ -14,6 +14,6 @@ jobs:
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v5
uses: SonarSource/sonarqube-scan-action@v6
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v4
with:
# Upload entire repository
path: './ppa'
+3 -3
View File
@@ -8,6 +8,6 @@ jobs:
steps:
- uses: actions/first-interaction@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: 'Thank you for raising this issue'
pr-message: 'Thank you for making this first PR'
repo_token: ${{ secrets.GITHUB_TOKEN }}
issue_message: 'Thank you for raising this issue'
pr_message: 'Thank you for making this first PR'
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-api/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1
uses: mikefarah/yq@v4.48.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+110
View File
@@ -4,6 +4,116 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2026.1-niolo] (2026-01-13)
- bugfix: mozzarella -> niolo config migration ([#6259])
- chore: remove run DKG migration ([#6253])
- bugfix: reexposed 'derive_extended_private_key' ([#6247])
- Bump js-yaml from 3.14.1 to 3.14.2 in /sdk/typescript/codegen/contract-clients ([#6231])
- Statistics API v2 ([#6227])
- Bump golang.org/x/crypto from 0.39.0 to 0.45.0 in /nym-gateway-probe/netstack_ping ([#6220])
- Update chain registry link ([#6219])
- Bump glob from 10.3.4 to 10.5.0 in /documentation/scripts/post-process ([#6216])
- Bump js-yaml from 4.1.0 to 4.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6215])
- gateway-probe fixes for run-local ([#6212])
- chore: updated default endpoint for retrieving attestation.json ([#6207])
- chore: remove support for legacy mixnode within the performance contract ([#6205])
- feat: upgrade mode: VPN adjustments ([#6189])
- Bump min-document from 2.19.0 to 2.19.1 ([#6181])
- Bump next from 15.4.1 to 15.4.7 in /nym-node-status-api/nym-node-status-ui ([#6180])
- feat: merge intermediate upgrade mode changes ([#6174])
- Add weighted scoring to NS API ([#6144])
- build(deps): bump mikefarah/yq from 4.47.1 to 4.48.1 ([#6107])
- build(deps): bump SonarSource/sonarqube-scan-action from 5 to 6 in /.github/workflows ([#6068])
- build(deps): bump tar-fs from 3.0.9 to 3.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6063])
- build(deps): bump ammonia from 4.1.1 to 4.1.2 ([#6057])
- build(deps): bump tower-http from 0.5.2 to 0.6.6 ([#6030])
- build(deps): bump actions/setup-go from 5 to 6 ([#6013])
- build(deps): bump next from 14.2.28 to 14.2.32 ([#5996])
- build(deps): bump tracing-subscriber from 0.3.19 to 0.3.20 ([#5993])
- build(deps): bump actions/upload-pages-artifact from 3 to 4 ([#5992])
[#6259]: https://github.com/nymtech/nym/pull/6259
[#6253]: https://github.com/nymtech/nym/pull/6253
[#6247]: https://github.com/nymtech/nym/pull/6247
[#6231]: https://github.com/nymtech/nym/pull/6231
[#6227]: https://github.com/nymtech/nym/pull/6227
[#6220]: https://github.com/nymtech/nym/pull/6220
[#6219]: https://github.com/nymtech/nym/pull/6219
[#6216]: https://github.com/nymtech/nym/pull/6216
[#6215]: https://github.com/nymtech/nym/pull/6215
[#6212]: https://github.com/nymtech/nym/pull/6212
[#6207]: https://github.com/nymtech/nym/pull/6207
[#6205]: https://github.com/nymtech/nym/pull/6205
[#6189]: https://github.com/nymtech/nym/pull/6189
[#6181]: https://github.com/nymtech/nym/pull/6181
[#6180]: https://github.com/nymtech/nym/pull/6180
[#6174]: https://github.com/nymtech/nym/pull/6174
[#6144]: https://github.com/nymtech/nym/pull/6144
[#6107]: https://github.com/nymtech/nym/pull/6107
[#6068]: https://github.com/nymtech/nym/pull/6068
[#6063]: https://github.com/nymtech/nym/pull/6063
[#6057]: https://github.com/nymtech/nym/pull/6057
[#6030]: https://github.com/nymtech/nym/pull/6030
[#6013]: https://github.com/nymtech/nym/pull/6013
[#5996]: https://github.com/nymtech/nym/pull/5996
[#5993]: https://github.com/nymtech/nym/pull/5993
[#5992]: https://github.com/nymtech/nym/pull/5992
## [2025.21-mozzarella] (2025-11-25)
- [bugfix] Tunnel not waiting on MixnetClient to shut down cleanly ([#6225])
- bugfix: fix credential proxy upgrade mode attestation url arg ([#6202])
- HTTP API resilience enable & domain rotation conditions ([#6200])
- Remove debug feature from http-macro spec in gateway probe ([#6195])
- DNS relibility and troubleshooting ([#6179])
- [bugfix] Distinguish authenticator errors by credential spent ([#6176])
- Typescript SDK 1.4.1 ([#6146])
- Enable URL rotation and retries for mixnet gateway init ([#6126])
- Feature/credential proxy jwt ([#5957])
[#6225]: https://github.com/nymtech/nym/pull/6225
[#6202]: https://github.com/nymtech/nym/pull/6202
[#6200]: https://github.com/nymtech/nym/pull/6200
[#6195]: https://github.com/nymtech/nym/pull/6195
[#6179]: https://github.com/nymtech/nym/pull/6179
[#6176]: https://github.com/nymtech/nym/pull/6176
[#6146]: https://github.com/nymtech/nym/pull/6146
[#6126]: https://github.com/nymtech/nym/pull/6126
[#5957]: https://github.com/nymtech/nym/pull/5957
## [2025.20-leerdammer] (2025-11-12)
- Max/tweak ts sdk actions ([#6185])
- chore: resolve clippy 1.91 warnings ([#6168])
- [chore] Remove unused dependencies ([#6151])
- Use typed-builder for registration client builder config ([#6150])
- tommy is too quick ([#6149])
- configurable mixnet client startup timeout ([#6148])
- [Feature/operators]: QUIC bridge deployment script v2 ([#6145])
- Bugfix: Add circuit breaker ([#6143])
- bugfix: update internal owner address in transferred share ([#6139])
- Update quic_bridge_deployment.sh for IPv4 and .deb package ([#6138])
- feat: expose more explicit new_with_fronted_urls builder for http API client ([#6136])
- bugfix: update stored epoch share when changing ownership ([#6135])
- Domain fronting ([#6134])
- bugfix: update stored epoch share when changing announce address ([#6131])
[#6185]: https://github.com/nymtech/nym/pull/6185
[#6168]: https://github.com/nymtech/nym/pull/6168
[#6151]: https://github.com/nymtech/nym/pull/6151
[#6150]: https://github.com/nymtech/nym/pull/6150
[#6149]: https://github.com/nymtech/nym/pull/6149
[#6148]: https://github.com/nymtech/nym/pull/6148
[#6145]: https://github.com/nymtech/nym/pull/6145
[#6143]: https://github.com/nymtech/nym/pull/6143
[#6139]: https://github.com/nymtech/nym/pull/6139
[#6138]: https://github.com/nymtech/nym/pull/6138
[#6136]: https://github.com/nymtech/nym/pull/6136
[#6135]: https://github.com/nymtech/nym/pull/6135
[#6134]: https://github.com/nymtech/nym/pull/6134
[#6131]: https://github.com/nymtech/nym/pull/6131
## [2025.19-kase] (2025-10-30)
- update ns agent workflow ([#6154])
Generated
+65 -96
View File
@@ -1283,7 +1283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -3965,7 +3965,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -4374,11 +4374,11 @@ dependencies = [
[[package]]
name = "matchers"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata 0.1.10",
"regex-automata",
]
[[package]]
@@ -4741,6 +4741,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
@@ -4825,7 +4834,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.68"
version = "1.1.71"
dependencies = [
"anyhow",
"async-trait",
@@ -4896,7 +4905,7 @@ dependencies = [
"tokio",
"tokio-stream",
"tokio-util",
"tower-http 0.5.2",
"tower-http",
"tracing",
"ts-rs",
"url",
@@ -4988,6 +4997,7 @@ dependencies = [
"nym-network-defaults",
"nym-service-provider-requests-common",
"nym-sphinx",
"nym-test-utils",
"nym-wireguard-types",
"rand 0.8.5",
"semver 1.0.26",
@@ -4995,6 +5005,7 @@ dependencies = [
"sha2 0.10.9",
"strum_macros",
"thiserror 2.0.12",
"tracing",
"x25519-dalek",
]
@@ -5003,21 +5014,16 @@ name = "nym-bandwidth-controller"
version = "0.1.0"
dependencies = [
"async-trait",
"bip39",
"log",
"nym-credential-storage",
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-time",
"nym-network-defaults",
"nym-task",
"nym-validator-client",
"rand 0.8.5",
"thiserror 2.0.12",
"url",
"zeroize",
]
[[package]]
@@ -5051,7 +5057,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.65"
version = "1.1.68"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -5134,7 +5140,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.65"
version = "1.1.68"
dependencies = [
"bs58",
"clap",
@@ -5481,7 +5487,7 @@ dependencies = [
"time",
"tokio",
"tokio-util",
"tower-http 0.5.2",
"tower-http",
"tracing",
"url",
"utoipa",
@@ -5570,6 +5576,7 @@ dependencies = [
"sqlx",
"sqlx-pool-guard",
"thiserror 2.0.12",
"time",
"tokio",
"zeroize",
]
@@ -5605,12 +5612,13 @@ dependencies = [
"nym-api-requests",
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-contract-common",
"nym-gateway-requests",
"nym-gateway-storage",
"nym-task",
"nym-upgrade-mode-check",
"nym-validator-client",
"rand 0.8.5",
"si-scale",
"thiserror 2.0.12",
"time",
@@ -5650,6 +5658,7 @@ dependencies = [
"nym-compact-ecash",
"nym-ecash-time",
"nym-network-defaults",
"nym-upgrade-mode-check",
"rand 0.8.5",
"serde",
"strum",
@@ -5800,7 +5809,6 @@ dependencies = [
name = "nym-gateway"
version = "1.1.36"
dependencies = [
"anyhow",
"async-trait",
"bincode",
"bip39",
@@ -5811,7 +5819,6 @@ dependencies = [
"futures",
"ipnetwork",
"mock_instant",
"nym-api-requests",
"nym-authenticator-requests",
"nym-client-core",
"nym-credential-verification",
@@ -5824,7 +5831,6 @@ dependencies = [
"nym-id",
"nym-ip-packet-router",
"nym-mixnet-client",
"nym-mixnode-common",
"nym-network-defaults",
"nym-network-requester",
"nym-node-metrics",
@@ -5834,20 +5840,18 @@ dependencies = [
"nym-statistics-common",
"nym-task",
"nym-topology",
"nym-types",
"nym-upgrade-mode-check",
"nym-validator-client",
"nym-wireguard",
"nym-wireguard-private-metadata-server",
"nym-wireguard-types",
"rand 0.8.5",
"serde",
"sha2 0.10.9",
"thiserror 2.0.12",
"time",
"tokio",
"tokio-stream",
"tokio-tungstenite",
"tokio-util",
"tracing",
"url",
"zeroize",
@@ -6058,6 +6062,7 @@ dependencies = [
"thiserror 2.0.12",
"tokio",
"tracing",
"tracing-subscriber",
"url",
"wasmtimer",
]
@@ -6362,7 +6367,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.66"
version = "1.1.69"
dependencies = [
"addr",
"anyhow",
@@ -6412,7 +6417,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.20.0"
version = "1.23.0"
dependencies = [
"anyhow",
"arc-swap",
@@ -6443,6 +6448,7 @@ dependencies = [
"nym-bin-common",
"nym-client-core-config-types",
"nym-config",
"nym-credential-verification",
"nym-crypto",
"nym-gateway",
"nym-gateway-stats-storage",
@@ -6485,7 +6491,7 @@ dependencies = [
"tokio-stream",
"tokio-util",
"toml 0.8.23",
"tower-http 0.5.2",
"tower-http",
"tracing",
"tracing-indicatif",
"tracing-subscriber",
@@ -6515,13 +6521,13 @@ version = "0.1.0"
dependencies = [
"async-trait",
"celes",
"humantime",
"humantime-serde",
"nym-bin-common",
"nym-crypto",
"nym-exit-policy",
"nym-http-api-client",
"nym-noise-keys",
"nym-upgrade-mode-check",
"nym-wireguard-types",
"rand_chacha 0.3.1",
"schemars 0.8.22",
@@ -6532,6 +6538,7 @@ dependencies = [
"thiserror 2.0.12",
"time",
"tokio",
"url",
"utoipa",
]
@@ -6554,7 +6561,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "4.0.11-rc1"
version = "4.0.12"
dependencies = [
"ammonia",
"anyhow",
@@ -6602,7 +6609,7 @@ dependencies = [
"tokio",
"tokio-stream",
"tokio-util",
"tower-http 0.5.2",
"tower-http",
"tracing",
"tracing-log 0.2.0",
"tracing-subscriber",
@@ -6939,7 +6946,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.65"
version = "1.1.68"
dependencies = [
"bs58",
"clap",
@@ -7199,7 +7206,7 @@ dependencies = [
[[package]]
name = "nym-statistics-api"
version = "0.2.1"
version = "0.3.0"
dependencies = [
"anyhow",
"axum",
@@ -7213,13 +7220,12 @@ dependencies = [
"nym-statistics-common",
"nym-task",
"nym-validator-client",
"serde",
"serde_json",
"sqlx",
"time",
"tokio",
"tokio-util",
"tower-http 0.5.2",
"tower-http",
"tracing",
"tracing-subscriber",
"url",
@@ -7400,6 +7406,7 @@ dependencies = [
name = "nym-validator-client"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"base64 0.22.1",
"bip32",
@@ -7573,17 +7580,10 @@ dependencies = [
name = "nym-wireguard"
version = "0.1.0"
dependencies = [
"async-trait",
"base64 0.22.1",
"bincode",
"chrono",
"dashmap",
"defguard_wireguard_rs",
"dyn-clone",
"futures",
"ip_network",
"log",
"nym-authenticator-requests",
"nym-credential-verification",
"nym-credentials-interface",
"nym-crypto",
@@ -7594,11 +7594,9 @@ dependencies = [
"nym-task",
"nym-wireguard-types",
"thiserror 2.0.12",
"time",
"tokio",
"tokio-stream",
"tracing",
"x25519-dalek",
]
[[package]]
@@ -7626,7 +7624,7 @@ dependencies = [
"nym-wireguard-private-metadata-shared",
"tokio",
"tokio-util",
"tower-http 0.5.2",
"tower-http",
"utoipa",
"utoipa-swagger-ui",
]
@@ -7650,16 +7648,21 @@ version = "1.0.0"
dependencies = [
"async-trait",
"axum",
"futures",
"nym-credential-verification",
"nym-credentials-interface",
"nym-crypto",
"nym-http-api-client",
"nym-http-api-common",
"nym-upgrade-mode-check",
"nym-wireguard",
"nym-wireguard-private-metadata-client",
"nym-wireguard-private-metadata-server",
"nym-wireguard-private-metadata-shared",
"time",
"tokio",
"tower-http 0.5.2",
"tower 0.5.2",
"tower-http",
"utoipa",
]
@@ -7668,10 +7671,7 @@ name = "nym-wireguard-types"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"log",
"nym-config",
"nym-crypto",
"nym-network-defaults",
"rand 0.8.5",
"serde",
"thiserror 2.0.12",
@@ -7680,7 +7680,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.30"
version = "0.1.33"
dependencies = [
"anyhow",
"bytes",
@@ -7731,7 +7731,7 @@ dependencies = [
"time",
"tokio",
"tokio-util",
"tower-http 0.5.2",
"tower-http",
"tracing",
"tracing-subscriber",
"utoipa",
@@ -8659,7 +8659,7 @@ dependencies = [
"once_cell",
"socket2 0.5.10",
"tracing",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -8820,17 +8820,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.9",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
"regex-automata",
"regex-syntax",
]
[[package]]
@@ -8841,15 +8832,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.5",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.5"
@@ -8929,7 +8914,7 @@ dependencies = [
"tokio-rustls 0.26.2",
"tokio-util",
"tower 0.5.2",
"tower-http 0.6.6",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
@@ -9129,7 +9114,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -10445,7 +10430,7 @@ dependencies = [
"getrandom 0.3.3",
"once_cell",
"rustix 1.0.8",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -11064,9 +11049,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.5.2"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
"async-compression",
"bitflags 2.9.1",
@@ -11078,33 +11063,17 @@ dependencies = [
"http-body-util",
"http-range-header",
"httpdate",
"iri-string",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-http"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
"bitflags 2.9.1",
"bytes",
"futures-util",
"http 1.3.1",
"http-body 1.0.1",
"iri-string",
"pin-project-lite",
"tower 0.5.2",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@@ -11212,14 +11181,14 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
"nu-ansi-term",
"nu-ansi-term 0.50.1",
"once_cell",
"regex",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
@@ -11255,7 +11224,7 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d"
dependencies = [
"nu-ansi-term",
"nu-ansi-term 0.46.0",
"tracing-core",
"tracing-log 0.1.4",
"tracing-subscriber",
@@ -12257,7 +12226,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
+3 -2
View File
@@ -171,6 +171,7 @@ members = [
default-members = [
"clients/native",
"clients/socks5",
"nym-authenticator-client",
"nym-api",
"nym-credential-proxy/nym-credential-proxy",
"nym-node",
@@ -346,11 +347,11 @@ tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.15"
toml = "0.8.22"
tower = "0.5.2"
tower-http = "0.5.2"
tower-http = "0.6.6"
tracing = "0.1.41"
tracing-log = "0.2"
tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19"
tracing-subscriber = "0.3.20"
tracing-tree = "0.2.2"
tracing-indicatif = "0.3.9"
tracing-test = "0.2.5"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.65"
version = "1.1.68"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.65"
version = "1.1.68"
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"
+7
View File
@@ -16,6 +16,7 @@ serde = { workspace = true, features = ["derive"] }
semver = { workspace = true }
strum_macros = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
nym-credentials-interface = { path = "../credentials-interface" }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
@@ -29,7 +30,13 @@ hmac = { workspace = true, optional = true }
sha2 = { workspace = true, optional = true }
x25519-dalek = { workspace = true, features = ["static_secrets"] }
[dev-dependencies]
nym-test-utils = { path = "../test-utils" }
[features]
default = ["verify"]
# this is moved to a separate feature as we really need clients to import it (especially, *cough*, wasm)
verify = ["hmac", "sha2"]
[lints]
workspace = true
@@ -6,9 +6,11 @@ use nym_wireguard_types::PeerPublicKey;
use crate::{
AuthenticatorVersion, Error,
latest::registration::IpPair,
traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, Versionable},
v2, v3, v4, v5,
traits::{
FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, UpgradeModeMessage,
Versionable,
},
v2, v3, v4, v5, v6,
};
// This is very redundant with AuthenticatorRequest and I reckon they could be smooshed.
@@ -19,6 +21,293 @@ pub enum ClientMessage {
Final(Box<dyn FinalMessage + Send + Sync + 'static>),
Query(Box<dyn QueryBandwidthMessage + Send + Sync + 'static>),
TopUp(Box<dyn TopUpMessage + Send + Sync + 'static>),
UpgradeModeCheck(Box<dyn UpgradeModeMessage + Send + Sync + 'static>),
}
pub struct SerialisedRequest {
pub bytes: Vec<u8>,
pub request_id: u64,
}
impl SerialisedRequest {
pub fn new(bytes: Vec<u8>, request_id: u64) -> Self {
Self { bytes, request_id }
}
}
impl ClientMessage {
fn serialise_v1(&self) -> Result<SerialisedRequest, Error> {
Err(Error::UnsupportedVersion)
}
fn serialise_v2(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
use v2::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ip: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message
.credential()
.and_then(|c| c.credential.into_zk_nym())
.map(|c| *c),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) =
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
_ => Err(Error::UnsupportedMessage),
}
}
fn serialise_v3(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
use v3::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ip: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message
.credential()
.and_then(|c| c.credential.into_zk_nym())
.map(|c| *c),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) =
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(
TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
_ => Err(Error::UnsupportedMessage),
}
}
fn serialise_v4(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
use v4::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ips: IpPair {
ipv4: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?,
ipv6: final_message
.gateway_client_ipv6()
.ok_or(Error::UnsupportedMessage)?,
},
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message
.credential()
.and_then(|c| c.credential.into_zk_nym())
.map(|c| *c),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) =
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(
TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
},
reply_to,
);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
_ => Err(Error::UnsupportedMessage),
}
}
fn serialise_v5(&self) -> Result<SerialisedRequest, Error> {
use v5::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
pub_key: init_message.pub_key(),
});
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ips: IpPair {
ipv4: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?,
ipv6: final_message
.gateway_client_ipv6()
.ok_or(Error::UnsupportedMessage)?,
},
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message
.credential()
.and_then(|c| c.credential.into_zk_nym())
.map(|c| *c),
});
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(query_message.pub_key());
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
});
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
_ => Err(Error::UnsupportedMessage),
}
}
fn serialise_v6(&self) -> Result<SerialisedRequest, Error> {
use v6::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
request::AuthenticatorRequest,
topup::TopUpMessage,
upgrade_mode_check::UpgradeModeCheckRequest,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
pub_key: init_message.pub_key(),
});
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ips: IpPair {
ipv4: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?,
ipv6: final_message
.gateway_client_ipv6()
.ok_or(Error::UnsupportedMessage)?,
},
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
});
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(query_message.pub_key());
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
});
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
ClientMessage::UpgradeModeCheck(upgrade_mode_check) => {
// currently JWT is the only emergency credential option
let Some(upgrade_mode_jwt) =
upgrade_mode_check.upgrade_mode_global_attestation_jwt()
else {
return Err(Error::conversion(
"no valid known upgrade mode check variants",
));
};
let msg = UpgradeModeCheckRequest::UpgradeModeJwt {
token: upgrade_mode_jwt,
};
let (req, id) = AuthenticatorRequest::new_upgrade_mode_check_request(msg);
Ok(SerialisedRequest::new(req.to_bytes()?, id))
}
}
}
}
impl ClientMessage {
@@ -27,7 +316,7 @@ impl ClientMessage {
match self {
Self::Final(msg) => msg.credential().is_some(),
Self::TopUp(_) => true,
Self::Initial(_) | Self::Query(_) => false,
Self::Initial(_) | Self::Query(_) | Self::UpgradeModeCheck(_) => false,
}
}
@@ -37,208 +326,18 @@ impl ClientMessage {
ClientMessage::Final(msg) => msg.version(),
ClientMessage::Query(msg) => msg.version(),
ClientMessage::TopUp(msg) => msg.version(),
ClientMessage::UpgradeModeCheck(msg) => msg.version(),
}
}
pub fn bytes(&self, reply_to: Recipient) -> Result<(Vec<u8>, u64), Error> {
pub fn bytes(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
match self.version() {
AuthenticatorVersion::V1 => Err(Error::UnsupportedVersion),
AuthenticatorVersion::V2 => {
use v2::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ip: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(
query_message.pub_key(),
reply_to,
);
Ok((req.to_bytes()?, id))
}
_ => Err(Error::UnsupportedMessage),
}
}
AuthenticatorVersion::V3 => {
use v3::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ip: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(
query_message.pub_key(),
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(
TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
}
}
AuthenticatorVersion::V4 => {
use v4::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ips: IpPair {
ipv4: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?,
ipv6: final_message
.gateway_client_ipv6()
.ok_or(Error::UnsupportedMessage)?,
}
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(
query_message.pub_key(),
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(
TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
}
}
AuthenticatorVersion::V5 => {
use v5::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
pub_key: init_message.pub_key(),
});
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ips: IpPair {
ipv4: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?,
ipv6: final_message
.gateway_client_ipv6()
.ok_or(Error::UnsupportedMessage)?,
},
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
});
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) =
AuthenticatorRequest::new_query_request(query_message.pub_key());
Ok((req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
});
Ok((req.to_bytes()?, id))
}
}
}
AuthenticatorVersion::V1 => self.serialise_v1(),
AuthenticatorVersion::V2 => self.serialise_v2(reply_to),
AuthenticatorVersion::V3 => self.serialise_v3(reply_to),
AuthenticatorVersion::V4 => self.serialise_v4(reply_to),
AuthenticatorVersion::V5 => self.serialise_v5(),
AuthenticatorVersion::V6 => self.serialise_v6(),
AuthenticatorVersion::UNKNOWN => Err(Error::UnknownVersion),
}
}
@@ -247,7 +346,7 @@ impl ClientMessage {
use AuthenticatorVersion::*;
match self.version() {
V1 | V2 | V3 | V4 => false,
V5 => true,
V5 | V6 => true,
UNKNOWN => true,
}
}
@@ -1,6 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::Display;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -37,3 +38,13 @@ pub enum Error {
#[error(transparent)]
Bincode(#[from] bincode::Error),
}
impl Error {
pub fn conversion(msg: impl Into<String>) -> Self {
Error::Conversion(msg.into())
}
pub fn conversion_display(msg: impl Display) -> Self {
Error::Conversion(msg.to_string())
}
}
+3 -1
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
pub mod client_message;
pub mod models;
pub mod request;
pub mod response;
pub mod traits;
@@ -10,13 +11,14 @@ pub mod v2;
pub mod v3;
pub mod v4;
pub mod v5;
pub mod v6;
mod error;
mod util;
mod version;
pub use error::Error;
pub use v5 as latest;
pub use v6 as latest;
pub use version::AuthenticatorVersion;
pub const CURRENT_VERSION: u8 = latest::VERSION;
@@ -0,0 +1,58 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials_interface::{
BandwidthCredential, CredentialSpendingData, TicketType, UnknownTicketType,
};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub enum CurrentUpgradeModeStatus {
Enabled,
Disabled,
// everything pre-v6
Unknown,
}
impl CurrentUpgradeModeStatus {
pub fn is_enabled(&self) -> bool {
matches!(self, CurrentUpgradeModeStatus::Enabled)
}
}
impl From<bool> for CurrentUpgradeModeStatus {
fn from(value: bool) -> Self {
if value {
CurrentUpgradeModeStatus::Enabled
} else {
CurrentUpgradeModeStatus::Disabled
}
}
}
impl From<CurrentUpgradeModeStatus> for Option<bool> {
fn from(value: CurrentUpgradeModeStatus) -> Self {
match value {
CurrentUpgradeModeStatus::Enabled => Some(true),
CurrentUpgradeModeStatus::Disabled => Some(false),
CurrentUpgradeModeStatus::Unknown => None,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct BandwidthClaim {
pub credential: BandwidthCredential,
pub kind: TicketType,
}
impl TryFrom<CredentialSpendingData> for BandwidthClaim {
type Error = UnknownTicketType;
fn try_from(credential: CredentialSpendingData) -> Result<Self, Self::Error> {
Ok(BandwidthClaim {
kind: TicketType::try_from_encoded(credential.payment.t_type)?,
credential: BandwidthCredential::from(credential),
})
}
}
+51 -2
View File
@@ -4,8 +4,10 @@
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::Recipient;
use crate::traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage};
use crate::{v1, v2, v3, v4, v5};
use crate::traits::{
FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, UpgradeModeMessage,
};
use crate::{v1, v2, v3, v4, v5, v6};
#[derive(Debug)]
pub enum AuthenticatorRequest {
@@ -33,6 +35,11 @@ pub enum AuthenticatorRequest {
reply_to: Option<Recipient>,
request_id: u64,
},
CheckUpgradeMode {
msg: Box<dyn UpgradeModeMessage + Send + Sync + 'static>,
protocol: Protocol,
request_id: u64,
},
}
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
@@ -202,3 +209,45 @@ impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
}
}
}
impl From<v6::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v6::request::AuthenticatorRequest) -> Self {
match value.data {
v6::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
},
v6::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
},
v6::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
}
}
v6::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
}
}
v6::request::AuthenticatorRequestData::CheckUpgradeMode(upgrade_mode_check_msg) => {
Self::CheckUpgradeMode {
msg: Box::new(upgrade_mode_check_msg),
protocol: value.protocol,
request_id: value.request_id,
}
}
}
}
}
+49 -2
View File
@@ -1,11 +1,12 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::models::CurrentUpgradeModeStatus;
use crate::traits::{
Id, PendingRegistrationResponse, RegisteredResponse, RemainingBandwidthResponse,
TopUpBandwidthResponse,
TopUpBandwidthResponse, UpgradeModeStatus,
};
use crate::{v2, v3, v4, v5};
use crate::{v2, v3, v4, v5, v6};
#[derive(Debug)]
pub enum AuthenticatorResponse {
@@ -13,6 +14,29 @@ pub enum AuthenticatorResponse {
Registered(Box<dyn RegisteredResponse + Send + Sync + 'static>),
RemainingBandwidth(Box<dyn RemainingBandwidthResponse + Send + Sync + 'static>),
TopUpBandwidth(Box<dyn TopUpBandwidthResponse + Send + Sync + 'static>),
UpgradeMode(Box<dyn UpgradeModeStatus + Send + Sync + 'static>),
}
impl UpgradeModeStatus for AuthenticatorResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
match self {
AuthenticatorResponse::PendingRegistration(pending_registration_response) => {
pending_registration_response.upgrade_mode_status()
}
AuthenticatorResponse::Registered(registered_response) => {
registered_response.upgrade_mode_status()
}
AuthenticatorResponse::RemainingBandwidth(remaining_bandwidth_response) => {
remaining_bandwidth_response.upgrade_mode_status()
}
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
top_up_bandwidth_response.upgrade_mode_status()
}
AuthenticatorResponse::UpgradeMode(upgrade_mode_response) => {
upgrade_mode_response.upgrade_mode_status()
}
}
}
}
impl Id for AuthenticatorResponse {
@@ -28,6 +52,7 @@ impl Id for AuthenticatorResponse {
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
top_up_bandwidth_response.id()
}
AuthenticatorResponse::UpgradeMode(upgrade_mode_response) => upgrade_mode_response.id(),
}
}
}
@@ -104,3 +129,25 @@ impl From<v5::response::AuthenticatorResponse> for AuthenticatorResponse {
}
}
}
impl From<v6::response::AuthenticatorResponse> for AuthenticatorResponse {
fn from(value: v6::response::AuthenticatorResponse) -> Self {
match value.data {
v6::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => Self::PendingRegistration(Box::new(pending_registration_response)),
v6::response::AuthenticatorResponseData::Registered(registered_response) => {
Self::Registered(Box::new(registered_response))
}
v6::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
v6::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
}
v6::response::AuthenticatorResponseData::UpgradeMode(upgrade_mode_check_response) => {
Self::UpgradeMode(Box::new(upgrade_mode_check_response))
}
}
}
}
+456 -34
View File
@@ -1,15 +1,15 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::latest::registration::IpPair;
use crate::models::{BandwidthClaim, CurrentUpgradeModeStatus};
use crate::{AuthenticatorVersion, Error, v1, v2, v3, v4, v5, v6};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::x25519;
use nym_wireguard_types::PeerPublicKey;
use std::fmt;
use std::net::{Ipv4Addr, Ipv6Addr};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::x25519::PrivateKey;
use nym_wireguard_types::PeerPublicKey;
use crate::latest::registration::IpPair;
use crate::{AuthenticatorVersion, Error, v1, v2, v3, v4, v5};
use tracing::error;
pub trait Versionable {
fn version(&self) -> AuthenticatorVersion;
@@ -51,6 +51,12 @@ impl Versionable for v5::registration::InitMessage {
}
}
impl Versionable for v6::registration::InitMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V6
}
}
impl Versionable for v2::registration::FinalMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V2
@@ -75,6 +81,12 @@ impl Versionable for v5::registration::FinalMessage {
}
}
impl Versionable for v6::registration::FinalMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V6
}
}
impl Versionable for PeerPublicKey {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V3
@@ -98,6 +110,158 @@ impl Versionable for v5::topup::TopUpMessage {
AuthenticatorVersion::V5
}
}
impl Versionable for v6::topup::TopUpMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V6
}
}
impl Versionable for v6::upgrade_mode_check::UpgradeModeCheckRequest {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V6
}
}
pub trait UpgradeModeStatus: Id + fmt::Debug {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus;
}
impl UpgradeModeStatus for v1::response::PendingRegistrationResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v1::response::RegisteredResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v1::response::RemainingBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v2::response::PendingRegistrationResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v2::response::RegisteredResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v2::response::RemainingBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v3::response::PendingRegistrationResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v3::response::RegisteredResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v3::response::RemainingBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v3::response::TopUpBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v4::response::PendingRegistrationResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v4::response::RegisteredResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v4::response::RemainingBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v4::response::TopUpBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v5::response::PendingRegistrationResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v5::response::RegisteredResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v5::response::RemainingBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v5::response::TopUpBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
CurrentUpgradeModeStatus::Unknown
}
}
impl UpgradeModeStatus for v6::response::PendingRegistrationResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
self.upgrade_mode_enabled.into()
}
}
impl UpgradeModeStatus for v6::response::RegisteredResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
self.upgrade_mode_enabled.into()
}
}
impl UpgradeModeStatus for v6::response::RemainingBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
self.upgrade_mode_enabled.into()
}
}
impl UpgradeModeStatus for v6::response::TopUpBandwidthResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
self.upgrade_mode_enabled.into()
}
}
impl UpgradeModeStatus for v6::response::UpgradeModeResponse {
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
self.upgrade_mode_enabled.into()
}
}
pub trait InitMessage: Versionable + fmt::Debug {
fn pub_key(&self) -> PeerPublicKey;
@@ -133,14 +297,20 @@ impl InitMessage for v5::registration::InitMessage {
}
}
impl InitMessage for v6::registration::InitMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
pub trait FinalMessage: Versionable + fmt::Debug {
fn gateway_client_pub_key(&self) -> PeerPublicKey;
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error>;
fn private_ips(&self) -> IpPair;
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr>;
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr>;
fn gateway_client_mac(&self) -> Vec<u8>;
fn credential(&self) -> Option<CredentialSpendingData>;
fn credential(&self) -> Option<BandwidthClaim>;
}
impl FinalMessage for v1::GatewayClient {
@@ -148,7 +318,7 @@ impl FinalMessage for v1::GatewayClient {
self.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
self.verify(private_key, nonce)
}
@@ -171,7 +341,7 @@ impl FinalMessage for v1::GatewayClient {
self.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> {
fn credential(&self) -> Option<BandwidthClaim> {
None
}
}
@@ -181,7 +351,7 @@ impl FinalMessage for v2::registration::FinalMessage {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
@@ -204,8 +374,12 @@ impl FinalMessage for v2::registration::FinalMessage {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone()
fn credential(&self) -> Option<BandwidthClaim> {
self.credential.clone().and_then(|c| {
c.try_into()
.inspect_err(|err| error!("credential conversion error: {err}"))
.ok()
})
}
}
@@ -214,7 +388,7 @@ impl FinalMessage for v3::registration::FinalMessage {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
@@ -237,8 +411,12 @@ impl FinalMessage for v3::registration::FinalMessage {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone()
fn credential(&self) -> Option<BandwidthClaim> {
self.credential.clone().and_then(|c| {
c.try_into()
.inspect_err(|err| error!("credential conversion error: {err}"))
.ok()
})
}
}
@@ -247,7 +425,42 @@ impl FinalMessage for v4::registration::FinalMessage {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
fn private_ips(&self) -> IpPair {
// v4 -> v5 -> v6
v5::registration::IpPair::from(self.gateway_client.private_ips).into()
}
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
Some(self.gateway_client.private_ips.ipv4)
}
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
Some(self.gateway_client.private_ips.ipv6)
}
fn gateway_client_mac(&self) -> Vec<u8> {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<BandwidthClaim> {
self.credential.clone().and_then(|c| {
c.try_into()
.inspect_err(|err| error!("credential conversion error: {err}"))
.ok()
})
}
}
impl FinalMessage for v5::registration::FinalMessage {
fn gateway_client_pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
@@ -267,17 +480,21 @@ impl FinalMessage for v4::registration::FinalMessage {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone()
fn credential(&self) -> Option<BandwidthClaim> {
self.credential.clone().and_then(|c| {
c.try_into()
.inspect_err(|err| error!("credential conversion error: {err}"))
.ok()
})
}
}
impl FinalMessage for v5::registration::FinalMessage {
impl FinalMessage for v6::registration::FinalMessage {
fn gateway_client_pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
@@ -297,7 +514,7 @@ impl FinalMessage for v5::registration::FinalMessage {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> {
fn credential(&self) -> Option<BandwidthClaim> {
self.credential.clone()
}
}
@@ -347,10 +564,42 @@ impl TopUpMessage for v5::topup::TopUpMessage {
}
}
impl TopUpMessage for v6::topup::TopUpMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
fn credential(&self) -> CredentialSpendingData {
self.credential.clone()
}
}
pub trait UpgradeModeMessage: Versionable + fmt::Debug {
// the idea is to expose different types of emergency credentials here,
// like upgrade mode JWT, emergency threshold credential issued by signers, etc.
fn upgrade_mode_global_attestation_jwt(&self) -> Option<String>;
}
impl UpgradeModeMessage for v6::upgrade_mode_check::UpgradeModeCheckRequest {
fn upgrade_mode_global_attestation_jwt(&self) -> Option<String> {
use v6::upgrade_mode_check::UpgradeModeCheckRequest;
match self {
UpgradeModeCheckRequest::UpgradeModeJwt { token } => Some(token.clone()),
}
}
}
pub trait Id {
fn id(&self) -> u64;
}
impl Id for v1::response::PendingRegistrationResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v2::response::PendingRegistrationResponse {
fn id(&self) -> u64 {
self.request_id
@@ -375,6 +624,18 @@ impl Id for v5::response::PendingRegistrationResponse {
}
}
impl Id for v6::response::PendingRegistrationResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v1::response::RegisteredResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v2::response::RegisteredResponse {
fn id(&self) -> u64 {
self.request_id
@@ -399,6 +660,18 @@ impl Id for v5::response::RegisteredResponse {
}
}
impl Id for v6::response::RegisteredResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v1::response::RemainingBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v2::response::RemainingBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
@@ -423,6 +696,12 @@ impl Id for v5::response::RemainingBandwidthResponse {
}
}
impl Id for v6::response::RemainingBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v3::response::TopUpBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
@@ -441,11 +720,28 @@ impl Id for v5::response::TopUpBandwidthResponse {
}
}
pub trait PendingRegistrationResponse: Id + fmt::Debug {
impl Id for v6::response::TopUpBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v6::response::UpgradeModeResponse {
fn id(&self) -> u64 {
self.request_id
}
}
pub trait PendingRegistrationResponse: Id + UpgradeModeStatus + fmt::Debug {
fn nonce(&self) -> u64;
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error>;
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error>;
fn pub_key(&self) -> PeerPublicKey;
fn private_ips(&self) -> IpPair;
fn finalise_registration(
&self,
private_key: &x25519::PrivateKey,
credential: Option<BandwidthClaim>,
) -> Box<dyn FinalMessage + Send + Sync>;
}
impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
@@ -453,7 +749,7 @@ impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
@@ -464,6 +760,22 @@ impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ip.into()
}
fn finalise_registration(
&self,
private_key: &x25519::PrivateKey,
credential: Option<BandwidthClaim>,
) -> Box<dyn FinalMessage + Send + Sync> {
Box::new(v2::registration::FinalMessage {
gateway_client: v2::registration::GatewayClient::new(
private_key,
self.pub_key().inner(),
self.private_ips().ipv4.into(),
self.nonce(),
),
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
})
}
}
impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
@@ -471,7 +783,7 @@ impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
@@ -482,6 +794,22 @@ impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ip.into()
}
fn finalise_registration(
&self,
private_key: &x25519::PrivateKey,
credential: Option<BandwidthClaim>,
) -> Box<dyn FinalMessage + Send + Sync> {
Box::new(v3::registration::FinalMessage {
gateway_client: v3::registration::GatewayClient::new(
private_key,
self.pub_key().inner(),
self.private_ips().ipv4.into(),
self.nonce(),
),
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
})
}
}
impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
@@ -489,7 +817,42 @@ impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.gateway_data.pub_key
}
fn private_ips(&self) -> IpPair {
// v4 -> v5 -> v6
v5::registration::IpPair::from(self.reply.gateway_data.private_ips).into()
}
fn finalise_registration(
&self,
private_key: &x25519::PrivateKey,
credential: Option<BandwidthClaim>,
) -> Box<dyn FinalMessage + Send + Sync> {
Box::new(v4::registration::FinalMessage {
gateway_client: v4::registration::GatewayClient::new(
private_key,
self.pub_key().inner(),
self.reply.gateway_data.private_ips,
self.nonce(),
),
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
})
}
}
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
fn nonce(&self) -> u64 {
self.reply.nonce
}
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
@@ -500,14 +863,30 @@ impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ips.into()
}
fn finalise_registration(
&self,
private_key: &x25519::PrivateKey,
credential: Option<BandwidthClaim>,
) -> Box<dyn FinalMessage + Send + Sync> {
Box::new(v5::registration::FinalMessage {
gateway_client: v5::registration::GatewayClient::new(
private_key,
self.pub_key().inner(),
self.reply.gateway_data.private_ips,
self.nonce(),
),
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
})
}
}
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
impl PendingRegistrationResponse for v6::response::PendingRegistrationResponse {
fn nonce(&self) -> u64 {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
@@ -518,9 +897,25 @@ impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ips
}
fn finalise_registration(
&self,
private_key: &x25519::PrivateKey,
credential: Option<BandwidthClaim>,
) -> Box<dyn FinalMessage + Send + Sync> {
Box::new(v6::registration::FinalMessage {
gateway_client: v6::registration::GatewayClient::new(
private_key,
self.pub_key().inner(),
self.reply.gateway_data.private_ips,
self.nonce(),
),
credential,
})
}
}
pub trait RegisteredResponse: Id + fmt::Debug {
pub trait RegisteredResponse: Id + UpgradeModeStatus + fmt::Debug {
fn private_ips(&self) -> IpPair;
fn pub_key(&self) -> PeerPublicKey;
fn wg_port(&self) -> u16;
@@ -555,7 +950,8 @@ impl RegisteredResponse for v3::response::RegisteredResponse {
}
impl RegisteredResponse for v4::response::RegisteredResponse {
fn private_ips(&self) -> IpPair {
self.reply.private_ips.into()
// v4 -> v5 -> v6
v5::registration::IpPair::from(self.reply.private_ips).into()
}
fn pub_key(&self) -> PeerPublicKey {
@@ -568,6 +964,20 @@ impl RegisteredResponse for v4::response::RegisteredResponse {
}
impl RegisteredResponse for v5::response::RegisteredResponse {
fn private_ips(&self) -> IpPair {
self.reply.private_ips.into()
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.pub_key
}
fn wg_port(&self) -> u16 {
self.reply.wg_port
}
}
impl RegisteredResponse for v6::response::RegisteredResponse {
fn private_ips(&self) -> IpPair {
self.reply.private_ips
}
@@ -581,7 +991,7 @@ impl RegisteredResponse for v5::response::RegisteredResponse {
}
}
pub trait RemainingBandwidthResponse: Id + fmt::Debug {
pub trait RemainingBandwidthResponse: Id + UpgradeModeStatus + fmt::Debug {
fn available_bandwidth(&self) -> Option<i64>;
}
@@ -609,7 +1019,13 @@ impl RemainingBandwidthResponse for v5::response::RemainingBandwidthResponse {
}
}
pub trait TopUpBandwidthResponse: Id + fmt::Debug {
impl RemainingBandwidthResponse for v6::response::RemainingBandwidthResponse {
fn available_bandwidth(&self) -> Option<i64> {
self.reply.as_ref().map(|r| r.available_bandwidth)
}
}
pub trait TopUpBandwidthResponse: Id + UpgradeModeStatus + fmt::Debug {
fn available_bandwidth(&self) -> i64;
}
@@ -630,3 +1046,9 @@ impl TopUpBandwidthResponse for v5::response::TopUpBandwidthResponse {
self.reply.available_bandwidth
}
}
impl TopUpBandwidthResponse for v6::response::TopUpBandwidthResponse {
fn available_bandwidth(&self) -> i64 {
self.reply.available_bandwidth
}
}
@@ -48,7 +48,7 @@ pub struct RegistrationData {
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData {
pub struct RegisteredData {
pub pub_key: PeerPublicKey,
pub private_ip: IpAddr,
pub wg_port: u16,
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
use nym_sphinx::addressing::Recipient;
use serde::{Deserialize, Serialize};
@@ -34,7 +34,7 @@ impl AuthenticatorResponse {
}
pub fn new_registered(
registred_data: RegistredData,
registred_data: RegisteredData,
reply_to: Recipient,
request_id: u64,
) -> Self {
@@ -108,7 +108,7 @@ pub struct PendingRegistrationResponse {
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
pub reply: RegisteredData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -154,8 +154,8 @@ impl From<v2::registration::RegistrationData> for v1::registration::Registration
}
}
impl From<v2::registration::RegistredData> for v1::registration::RegistredData {
fn from(value: v2::registration::RegistredData) -> Self {
impl From<v2::registration::RegisteredData> for v1::registration::RegisteredData {
fn from(value: v2::registration::RegisteredData) -> Self {
Self {
pub_key: value.pub_key,
private_ip: value.private_ip,
@@ -58,7 +58,7 @@ pub struct RegistrationData {
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistredData {
pub struct RegisteredData {
pub pub_key: PeerPublicKey,
pub private_ip: IpAddr,
pub wg_port: u16,
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::Recipient;
use serde::{Deserialize, Serialize};
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
}
pub fn new_registered(
registred_data: RegistredData,
registred_data: RegisteredData,
reply_to: Recipient,
request_id: u64,
) -> Self {
@@ -118,7 +118,7 @@ pub struct PendingRegistrationResponse {
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
pub reply: RegisteredData,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@@ -299,8 +299,8 @@ impl From<v2::registration::RegistrationData> for v3::registration::Registration
}
}
impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
fn from(value: v3::registration::RegistredData) -> Self {
impl From<v3::registration::RegisteredData> for v2::registration::RegisteredData {
fn from(value: v3::registration::RegisteredData) -> Self {
Self {
pub_key: value.pub_key,
private_ip: value.private_ip,
@@ -309,8 +309,8 @@ impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
}
}
impl From<v2::registration::RegistredData> for v3::registration::RegistredData {
fn from(value: v2::registration::RegistredData) -> Self {
impl From<v2::registration::RegisteredData> for v3::registration::RegisteredData {
fn from(value: v2::registration::RegisteredData) -> Self {
Self {
pub_key: value.pub_key,
private_ip: value.private_ip,
@@ -674,7 +674,7 @@ mod tests {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v2::registration::RegistredData {
let registred_data = v2::registration::RegisteredData {
pub_key,
private_ip,
wg_port,
@@ -701,7 +701,7 @@ mod tests {
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
reply: v3::registration::RegisteredData {
wg_port,
pub_key,
private_ip
@@ -715,7 +715,7 @@ mod tests {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
let registred_data = v3::registration::RegisteredData {
pub_key,
private_ip,
wg_port,
@@ -742,7 +742,7 @@ mod tests {
v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse {
request_id,
reply_to,
reply: v2::registration::RegistredData {
reply: v2::registration::RegisteredData {
wg_port,
pub_key,
private_ip
@@ -58,7 +58,7 @@ pub struct RegistrationData {
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistredData {
pub struct RegisteredData {
pub pub_key: PeerPublicKey,
pub private_ip: IpAddr,
pub wg_port: u16,
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::Recipient;
use serde::{Deserialize, Serialize};
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
}
pub fn new_registered(
registred_data: RegistredData,
registred_data: RegisteredData,
reply_to: Recipient,
request_id: u64,
) -> Self {
@@ -139,7 +139,7 @@ pub struct PendingRegistrationResponse {
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
pub reply: RegisteredData,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@@ -262,8 +262,8 @@ impl From<v4::response::TopUpBandwidthResponse> for v3::response::TopUpBandwidth
}
}
impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
fn from(value: v3::registration::RegistredData) -> Self {
impl From<v3::registration::RegisteredData> for v4::registration::RegisteredData {
fn from(value: v3::registration::RegisteredData) -> Self {
Self {
pub_key: value.pub_key,
private_ips: value.private_ip.into(),
@@ -272,8 +272,8 @@ impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
}
}
impl From<v4::registration::RegistredData> for v3::registration::RegistredData {
fn from(value: v4::registration::RegistredData) -> Self {
impl From<v4::registration::RegisteredData> for v3::registration::RegisteredData {
fn from(value: v4::registration::RegisteredData) -> Self {
Self {
pub_key: value.pub_key,
private_ip: value.private_ips.ipv4.into(),
@@ -565,7 +565,7 @@ mod tests {
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap());
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
let registred_data = v3::registration::RegisteredData {
pub_key,
private_ip: ipv4.into(),
wg_port,
@@ -592,7 +592,7 @@ mod tests {
v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse {
request_id,
reply_to,
reply: v4::registration::RegistredData {
reply: v4::registration::RegisteredData {
wg_port,
pub_key,
private_ips
@@ -608,7 +608,7 @@ mod tests {
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let wg_port = 51822;
let registred_data = v4::registration::RegistredData {
let registred_data = v4::registration::RegisteredData {
pub_key,
private_ips,
wg_port,
@@ -635,7 +635,7 @@ mod tests {
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
reply: v3::registration::RegisteredData {
wg_port,
pub_key,
private_ip: ipv4.into()
@@ -110,7 +110,7 @@ pub struct RegistrationData {
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistredData {
pub struct RegisteredData {
pub pub_key: PeerPublicKey,
pub private_ips: IpPair,
pub wg_port: u16,
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::Recipient;
use serde::{Deserialize, Serialize};
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
}
pub fn new_registered(
registred_data: RegistredData,
registred_data: RegisteredData,
reply_to: Recipient,
request_id: u64,
) -> Self {
@@ -139,7 +139,7 @@ pub struct PendingRegistrationResponse {
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
pub reply: RegisteredData,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@@ -186,8 +186,8 @@ impl From<v4::response::TopUpBandwidthResponse> for v5::response::TopUpBandwidth
}
}
impl From<v4::registration::RegistredData> for v5::registration::RegistredData {
fn from(value: v4::registration::RegistredData) -> Self {
impl From<v4::registration::RegisteredData> for v5::registration::RegisteredData {
fn from(value: v4::registration::RegisteredData) -> Self {
Self {
pub_key: value.pub_key,
private_ips: value.private_ips.into(),
@@ -405,7 +405,7 @@ mod tests {
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
let private_ips = v4::registration::IpPair::new(ipv4, ipv6);
let wg_port = 51822;
let registred_data = v4::registration::RegistredData {
let registred_data = v4::registration::RegisteredData {
pub_key,
private_ips,
wg_port,
@@ -431,7 +431,7 @@ mod tests {
upgraded_msg.data,
v5::response::AuthenticatorResponseData::Registered(v5::response::RegisteredResponse {
request_id,
reply: v5::registration::RegistredData {
reply: v5::registration::RegisteredData {
wg_port,
pub_key,
private_ips: v5::registration::IpPair::new(ipv4, ipv6)
@@ -108,7 +108,7 @@ pub struct RegistrationData {
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistredData {
pub struct RegisteredData {
pub pub_key: PeerPublicKey,
pub private_ips: IpPair,
pub wg_port: u16,
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use serde::{Deserialize, Serialize};
@@ -32,7 +32,7 @@ impl AuthenticatorResponse {
}
}
pub fn new_registered(registred_data: RegistredData, request_id: u64) -> Self {
pub fn new_registered(registred_data: RegisteredData, request_id: u64) -> Self {
Self {
protocol: Protocol {
service_provider_type: ServiceProviderType::Authenticator,
@@ -116,7 +116,7 @@ pub struct PendingRegistrationResponse {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RegisteredResponse {
pub request_id: u64,
pub reply: RegistredData,
pub reply: RegisteredData,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@@ -0,0 +1,441 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{v5, v6};
impl TryFrom<v5::request::AuthenticatorRequest> for v6::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v5::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: v6::PROTOCOL,
data: authenticator_request.data.try_into()?,
request_id: authenticator_request.request_id,
})
}
}
impl TryFrom<v5::request::AuthenticatorRequestData> for v6::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v5::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v5::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v6::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v5::request::AuthenticatorRequestData::Final(final_msg) => Ok(
v6::request::AuthenticatorRequestData::Final(Box::new((*final_msg).try_into()?)),
),
v5::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v6::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
v6::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
),
}
}
}
impl From<v5::registration::InitMessage> for v6::registration::InitMessage {
fn from(init_msg: v5::registration::InitMessage) -> Self {
Self {
pub_key: init_msg.pub_key,
}
}
}
impl TryFrom<v5::registration::FinalMessage> for v6::registration::FinalMessage {
type Error = crate::Error;
fn try_from(final_msg: v5::registration::FinalMessage) -> Result<Self, Self::Error> {
Ok(Self {
gateway_client: final_msg.gateway_client.into(),
credential: final_msg
.credential
.map(TryInto::try_into)
.transpose()
.map_err(Self::Error::conversion_display)?,
})
}
}
impl From<v5::registration::GatewayClient> for v6::registration::GatewayClient {
fn from(gateway_client: v5::registration::GatewayClient) -> Self {
Self {
pub_key: gateway_client.pub_key,
private_ips: gateway_client.private_ips.into(),
mac: gateway_client.mac.into(),
}
}
}
impl From<v6::registration::GatewayClient> for v5::registration::GatewayClient {
fn from(gateway_client: v6::registration::GatewayClient) -> Self {
Self {
pub_key: gateway_client.pub_key,
private_ips: gateway_client.private_ips.into(),
mac: gateway_client.mac.into(),
}
}
}
impl From<v5::registration::ClientMac> for v6::registration::ClientMac {
fn from(client_mac: v5::registration::ClientMac) -> Self {
Self::new((*client_mac).clone())
}
}
impl From<v6::registration::ClientMac> for v5::registration::ClientMac {
fn from(client_mac: v6::registration::ClientMac) -> Self {
Self::new((*client_mac).clone())
}
}
impl From<Box<v5::topup::TopUpMessage>> for Box<v6::topup::TopUpMessage> {
fn from(top_up_message: Box<v5::topup::TopUpMessage>) -> Self {
Box::new(v6::topup::TopUpMessage {
pub_key: top_up_message.pub_key,
credential: top_up_message.credential,
})
}
}
impl From<v5::response::AuthenticatorResponse> for v6::response::AuthenticatorResponse {
fn from(value: v5::response::AuthenticatorResponse) -> Self {
Self {
protocol: v6::PROTOCOL,
data: value.data.into(),
}
}
}
impl From<v5::response::AuthenticatorResponseData> for v6::response::AuthenticatorResponseData {
fn from(authenticator_response_data: v5::response::AuthenticatorResponseData) -> Self {
match authenticator_response_data {
v5::response::AuthenticatorResponseData::PendingRegistration(pending_response) => {
v6::response::AuthenticatorResponseData::PendingRegistration(
pending_response.into(),
)
}
v5::response::AuthenticatorResponseData::Registered(registered_response) => {
v6::response::AuthenticatorResponseData::Registered(registered_response.into())
}
v5::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => v6::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response.into(),
),
v5::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => {
v6::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into())
}
}
}
}
impl From<v5::response::RegisteredResponse> for v6::response::RegisteredResponse {
fn from(value: v5::response::RegisteredResponse) -> Self {
Self {
request_id: value.request_id,
reply: value.reply.into(),
upgrade_mode_enabled: false,
}
}
}
impl From<v5::response::PendingRegistrationResponse> for v6::response::PendingRegistrationResponse {
fn from(value: v5::response::PendingRegistrationResponse) -> Self {
Self {
request_id: value.request_id,
reply: value.reply.into(),
upgrade_mode_enabled: false,
}
}
}
impl From<v5::registration::RegistrationData> for v6::registration::RegistrationData {
fn from(value: v5::registration::RegistrationData) -> Self {
Self {
nonce: value.nonce,
gateway_data: value.gateway_data.into(),
wg_port: value.wg_port,
}
}
}
impl From<v6::registration::RegistrationData> for v5::registration::RegistrationData {
fn from(value: v6::registration::RegistrationData) -> Self {
Self {
nonce: value.nonce,
gateway_data: value.gateway_data.into(),
wg_port: value.wg_port,
}
}
}
impl From<v5::response::RemainingBandwidthResponse> for v6::response::RemainingBandwidthResponse {
fn from(value: v5::response::RemainingBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply: value.reply.map(Into::into),
upgrade_mode_enabled: false,
}
}
}
impl From<v5::response::TopUpBandwidthResponse> for v6::response::TopUpBandwidthResponse {
fn from(value: v5::response::TopUpBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply: value.reply.into(),
upgrade_mode_enabled: false,
}
}
}
impl From<v5::registration::RegisteredData> for v6::registration::RegisteredData {
fn from(value: v5::registration::RegisteredData) -> Self {
Self {
pub_key: value.pub_key,
private_ips: value.private_ips.into(),
wg_port: value.wg_port,
}
}
}
impl From<v5::registration::RemainingBandwidthData> for v6::registration::RemainingBandwidthData {
fn from(value: v5::registration::RemainingBandwidthData) -> Self {
Self {
available_bandwidth: value.available_bandwidth,
}
}
}
impl From<v5::registration::IpPair> for v6::registration::IpPair {
fn from(value: v5::registration::IpPair) -> Self {
Self {
ipv4: value.ipv4,
ipv6: value.ipv6,
}
}
}
impl From<v6::registration::IpPair> for v5::registration::IpPair {
fn from(value: v6::registration::IpPair) -> Self {
Self {
ipv4: value.ipv4,
ipv6: value.ipv6,
}
}
}
#[cfg(test)]
mod tests {
use std::{
net::{Ipv4Addr, Ipv6Addr},
str::FromStr,
};
use nym_credentials_interface::{BandwidthCredential, CredentialSpendingData, TicketType};
use nym_crypto::asymmetric::x25519::PrivateKey;
use nym_wireguard_types::PeerPublicKey;
use x25519_dalek::PublicKey;
use super::*;
use crate::models::BandwidthClaim;
use crate::{util::tests::CREDENTIAL_BYTES, v5};
#[test]
fn upgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let (msg, _) = v5::request::AuthenticatorRequest::new_initial_request(
v5::registration::InitMessage::new(pub_key),
);
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
assert_eq!(
upgraded_msg.data,
v6::request::AuthenticatorRequestData::Initial(v6::registration::InitMessage {
pub_key
})
);
}
#[test]
fn upgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
let ips = v5::registration::IpPair::new(ipv4, ipv6);
let nonce = 42;
let gateway_client = v5::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ips,
nonce,
);
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let final_message = v5::registration::FinalMessage {
gateway_client: gateway_client.clone(),
credential: Some(credential.clone()),
};
let (msg, _) = v5::request::AuthenticatorRequest::new_final_request(final_message);
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
assert_eq!(
upgraded_msg.data,
v6::request::AuthenticatorRequestData::Final(Box::new(
v6::registration::FinalMessage {
gateway_client: v6::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
v6::registration::IpPair::new(ipv4, ipv6),
nonce
),
credential: Some(BandwidthClaim {
credential: BandwidthCredential::ZkNym(Box::new(credential)),
kind: TicketType::V1MixnetEntry,
})
}
))
);
}
#[test]
fn upgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let (msg, _) = v5::request::AuthenticatorRequest::new_query_request(pub_key);
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
assert_eq!(
upgraded_msg.data,
v6::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn upgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
let ips = v5::registration::IpPair::new(ipv4, ipv6);
let nonce = 42;
let wg_port = 51822;
let gateway_data = v5::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ips,
nonce,
);
let registration_data = v5::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let msg = v5::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
);
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
assert_eq!(
upgraded_msg.data,
v6::response::AuthenticatorResponseData::PendingRegistration(
v6::response::PendingRegistrationResponse {
request_id,
reply: v6::registration::RegistrationData {
nonce,
gateway_data: v6::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
v6::registration::IpPair::new(ipv4, ipv6),
nonce
),
wg_port
},
upgrade_mode_enabled: false,
}
)
);
}
#[test]
fn upgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
let private_ips = v5::registration::IpPair::new(ipv4, ipv6);
let wg_port = 51822;
let registered_data = v5::registration::RegisteredData {
pub_key,
private_ips,
wg_port,
};
let request_id = 123;
let msg = v5::response::AuthenticatorResponse::new_registered(registered_data, request_id);
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
assert_eq!(
upgraded_msg.data,
v6::response::AuthenticatorResponseData::Registered(v6::response::RegisteredResponse {
request_id,
reply: v6::registration::RegisteredData {
wg_port,
pub_key,
private_ips: v6::registration::IpPair::new(ipv4, ipv6)
},
upgrade_mode_enabled: false,
})
);
}
#[test]
fn upgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v5::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let msg = v5::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
request_id,
);
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
assert_eq!(
upgraded_msg.data,
v6::response::AuthenticatorResponseData::RemainingBandwidth(
v6::response::RemainingBandwidthResponse {
request_id,
reply: Some(v6::registration::RemainingBandwidthData {
available_bandwidth,
}),
upgrade_mode_enabled: false,
}
)
);
}
}
@@ -0,0 +1,15 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
pub mod conversion;
pub mod registration;
pub mod request;
pub mod response;
pub mod topup;
pub mod upgrade_mode_check;
pub const VERSION: u8 = 6;
pub const PROTOCOL: Protocol = Protocol::new(VERSION, ServiceProviderType::Authenticator);
@@ -0,0 +1,287 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use crate::models::BandwidthClaim;
use base64::{Engine, engine::general_purpose};
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::time::SystemTime;
use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::x25519::{PrivateKey, PublicKey};
#[cfg(feature = "verify")]
use sha2::Sha256;
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
pub type PrivateIPs = HashMap<IpPair, Taken>;
#[cfg(feature = "verify")]
pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64;
pub type Taken = Option<SystemTime>;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
pub ipv4: Ipv4Addr,
pub ipv6: Ipv6Addr,
}
impl IpPair {
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
IpPair { ipv4, ipv6 }
}
}
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
IpPair { ipv4, ipv6 }
}
}
impl fmt::Display for IpPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.ipv4, self.ipv6)
}
}
impl From<IpAddr> for IpPair {
fn from(value: IpAddr) -> Self {
let (before_last_byte, last_byte) = match value {
IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]),
IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]),
};
let last_bytes = ((before_last_byte as u16) << 8) | last_byte as u16;
let ipv4 = Ipv4Addr::new(
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0],
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1],
before_last_byte,
last_byte,
);
let ipv6 = Ipv6Addr::new(
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[0],
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[1],
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[2],
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[3],
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[4],
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[5],
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[6],
last_bytes,
);
IpPair::new(ipv4, ipv6)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InitMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
}
impl InitMessage {
pub fn new(pub_key: PeerPublicKey) -> Self {
InitMessage { pub_key }
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct FinalMessage {
/// Gateway client data
pub gateway_client: GatewayClient,
/// Ecash credential
pub credential: Option<BandwidthClaim>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegistrationData {
pub nonce: u64,
pub gateway_data: GatewayClient,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RegisteredData {
pub pub_key: PeerPublicKey,
pub private_ips: IpPair,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RemainingBandwidthData {
pub available_bandwidth: i64,
}
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct GatewayClient {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
/// Assigned private IPs (v4 and v6)
pub private_ips: IpPair,
/// Sha256 hmac on the data (alongside the prior nonce)
pub mac: ClientMac,
}
impl GatewayClient {
#[cfg(feature = "verify")]
pub fn new(
local_secret: &PrivateKey,
remote_public: x25519_dalek::PublicKey,
private_ips: IpPair,
nonce: u64,
) -> Self {
let local_public = PublicKey::from(local_secret);
let remote_public = PublicKey::from(remote_public);
let dh = local_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(&dh[..])
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
mac.update(private_ips.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public.into()),
private_ips,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
}
// Reusable secret should be gateways Wireguard PK
// Client should perform this step when generating its payload, using its own WG PK
#[cfg(feature = "verify")]
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// use gateways key as a ref to an x25519_dalek key
let dh = gateway_key.inner().diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
.expect("x25519 shared secret is always 32 bytes long");
mac.update(self.pub_key.as_bytes());
mac.update(self.private_ips.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());
mac.verify_slice(&self.mac)
.map_err(|source| Error::FailedClientMacVerification {
client: self.pub_key.to_string(),
source,
})
}
pub fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone, PartialEq)]
pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", general_purpose::STANDARD.encode(&self.0))
}
}
impl From<Vec<u8>> for ClientMac {
fn from(v: Vec<u8>) -> Self {
ClientMac(v)
}
}
impl ClientMac {
#[allow(dead_code)]
pub fn new(mac: Vec<u8>) -> Self {
ClientMac(mac)
}
}
impl Deref for ClientMac {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl FromStr for ClientMac {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mac_bytes: Vec<u8> =
general_purpose::STANDARD
.decode(s)
.map_err(|source| Error::MalformedClientMac {
mac: s.to_string(),
source,
})?;
Ok(ClientMac(mac_bytes))
}
}
impl Serialize for ClientMac {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
serializer.serialize_str(&encoded_key)
}
}
impl<'de> Deserialize<'de> for ClientMac {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let encoded_key = String::deserialize(deserializer)?;
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use nym_crypto::asymmetric::x25519;
use nym_test_utils::helpers::deterministic_rng;
#[test]
fn create_ip_pair() {
let ipv4: IpAddr = Ipv4Addr::from_str("10.1.10.50").unwrap().into();
let ipv6: IpAddr = Ipv6Addr::from_str("fc01::0a32").unwrap().into();
assert_eq!(IpPair::from(ipv4), IpPair::from(ipv6));
}
#[test]
#[cfg(feature = "verify")]
fn client_request_roundtrip() {
let mut rng = deterministic_rng();
let gateway_key_pair = x25519::KeyPair::new(&mut rng);
let client_key_pair = x25519::KeyPair::new(&mut rng);
let nonce = 1234567890;
let client = GatewayClient::new(
client_key_pair.private_key(),
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
IpPair::new("10.0.0.42".parse().unwrap(), "fc00::42".parse().unwrap()),
nonce,
);
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
}
}
@@ -0,0 +1,135 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::{
PROTOCOL,
registration::{FinalMessage, InitMessage},
topup::TopUpMessage,
upgrade_mode_check::UpgradeModeCheckRequest,
};
use nym_service_provider_requests_common::Protocol;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
fn generate_random() -> u64 {
use rand::RngCore;
let mut rng = rand::rngs::OsRng;
rng.next_u64()
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct AuthenticatorRequest {
pub protocol: Protocol,
pub data: AuthenticatorRequestData,
pub request_id: u64,
}
impl AuthenticatorRequest {
pub fn from_reconstructed_message(
message: &nym_sphinx::receiver::ReconstructedMessage,
) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(&message.message)
}
pub fn new_initial_request(init_message: InitMessage) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: PROTOCOL,
data: AuthenticatorRequestData::Initial(init_message),
request_id,
},
request_id,
)
}
pub fn new_final_request(final_message: FinalMessage) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: PROTOCOL,
data: AuthenticatorRequestData::Final(Box::new(final_message)),
request_id,
},
request_id,
)
}
pub fn new_query_request(peer_public_key: PeerPublicKey) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: PROTOCOL,
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
request_id,
},
request_id,
)
}
pub fn new_topup_request(top_up_message: TopUpMessage) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: PROTOCOL,
data: AuthenticatorRequestData::TopUpBandwidth(Box::new(top_up_message)),
request_id,
},
request_id,
)
}
pub fn new_upgrade_mode_check_request(message: UpgradeModeCheckRequest) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: PROTOCOL,
data: AuthenticatorRequestData::CheckUpgradeMode(message),
request_id,
},
request_id,
)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
use bincode::Options;
make_bincode_serializer().serialize(self)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorRequestData {
Initial(InitMessage),
Final(Box<FinalMessage>),
QueryBandwidth(PeerPublicKey),
TopUpBandwidth(Box<TopUpMessage>),
CheckUpgradeMode(UpgradeModeCheckRequest),
}
#[cfg(test)]
mod tests {
use super::super::VERSION;
use super::*;
use nym_service_provider_requests_common::ServiceProviderType;
use std::str::FromStr;
#[test]
fn check_first_bytes_protocol() {
let version = VERSION;
let data = AuthenticatorRequest {
protocol: Protocol {
version,
service_provider_type: ServiceProviderType::Authenticator,
},
data: AuthenticatorRequestData::Initial(InitMessage::new(
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
)),
request_id: 1,
};
let bytes = *data.to_bytes().unwrap().first_chunk::<2>().unwrap();
assert_eq!(bytes, [version, ServiceProviderType::Authenticator as u8]);
}
}
@@ -0,0 +1,153 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
use nym_service_provider_requests_common::Protocol;
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
use super::PROTOCOL;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct AuthenticatorResponse {
pub protocol: Protocol,
pub data: AuthenticatorResponseData,
}
impl AuthenticatorResponse {
pub fn new_pending_registration_success(
registration_data: RegistrationData,
request_id: u64,
upgrade_mode_enabled: bool,
) -> Self {
Self {
protocol: PROTOCOL,
data: AuthenticatorResponseData::PendingRegistration(PendingRegistrationResponse {
reply: registration_data,
request_id,
upgrade_mode_enabled,
}),
}
}
pub fn new_registered(
registered_data: RegisteredData,
request_id: u64,
upgrade_mode_enabled: bool,
) -> Self {
Self {
protocol: PROTOCOL,
data: AuthenticatorResponseData::Registered(RegisteredResponse {
reply: registered_data,
request_id,
upgrade_mode_enabled,
}),
}
}
pub fn new_remaining_bandwidth(
remaining_bandwidth_data: Option<RemainingBandwidthData>,
request_id: u64,
upgrade_mode_enabled: bool,
) -> Self {
Self {
protocol: PROTOCOL,
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
reply: remaining_bandwidth_data,
request_id,
upgrade_mode_enabled,
}),
}
}
pub fn new_topup_bandwidth(
remaining_bandwidth_data: RemainingBandwidthData,
request_id: u64,
upgrade_mode_enabled: bool,
) -> Self {
Self {
protocol: PROTOCOL,
data: AuthenticatorResponseData::TopUpBandwidth(TopUpBandwidthResponse {
reply: remaining_bandwidth_data,
request_id,
upgrade_mode_enabled,
}),
}
}
pub fn new_upgrade_mode_check(request_id: u64, upgrade_mode_enabled: bool) -> Self {
Self {
protocol: PROTOCOL,
data: AuthenticatorResponseData::UpgradeMode(UpgradeModeResponse {
request_id,
upgrade_mode_enabled,
}),
}
}
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
use bincode::Options;
make_bincode_serializer().serialize(self)
}
pub fn from_reconstructed_message(
message: &nym_sphinx::receiver::ReconstructedMessage,
) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(&message.message)
}
pub fn id(&self) -> Option<u64> {
match &self.data {
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
AuthenticatorResponseData::TopUpBandwidth(response) => Some(response.request_id),
AuthenticatorResponseData::UpgradeMode(response) => Some(response.request_id),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse),
RemainingBandwidth(RemainingBandwidthResponse),
TopUpBandwidth(TopUpBandwidthResponse),
UpgradeMode(UpgradeModeResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PendingRegistrationResponse {
pub request_id: u64,
pub reply: RegistrationData,
pub upgrade_mode_enabled: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RegisteredResponse {
pub request_id: u64,
pub reply: RegisteredData,
pub upgrade_mode_enabled: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct RemainingBandwidthResponse {
pub request_id: u64,
pub reply: Option<RemainingBandwidthData>,
pub upgrade_mode_enabled: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TopUpBandwidthResponse {
pub request_id: u64,
pub reply: RemainingBandwidthData,
pub upgrade_mode_enabled: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct UpgradeModeResponse {
pub request_id: u64,
pub upgrade_mode_enabled: bool,
}
@@ -0,0 +1,15 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct TopUpMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
/// Ecash credential
pub credential: CredentialSpendingData,
}
@@ -0,0 +1,12 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum UpgradeModeCheckRequest {
/// Attempt to request upgrade mode recheck via the JWT issued as the result of
/// global attestation.json being published
UpgradeModeJwt { token: String },
}
+21 -5
View File
@@ -1,7 +1,7 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::{v1, v2, v3, v4, v5};
use super::{v1, v2, v3, v4, v5, v6};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::Display)]
@@ -22,11 +22,15 @@ pub enum AuthenticatorVersion {
/// introduced in dorina-patched release (1.6.1)
V5,
/// introduced in niolo release (1.23.0)
V6,
/// an unknown, future, variant that can be present if running outdated software
UNKNOWN,
}
impl AuthenticatorVersion {
pub const LATEST: Self = Self::V5;
pub const LATEST: Self = Self::V6;
pub const fn release_version(&self) -> semver::Version {
match self {
@@ -35,6 +39,7 @@ impl AuthenticatorVersion {
AuthenticatorVersion::V3 => semver::Version::new(1, 1, 10),
AuthenticatorVersion::V4 => semver::Version::new(1, 2, 0),
AuthenticatorVersion::V5 => semver::Version::new(1, 6, 1),
AuthenticatorVersion::V6 => semver::Version::new(1, 23, 0),
AuthenticatorVersion::UNKNOWN => semver::Version::new(0, 0, 0),
}
}
@@ -54,6 +59,8 @@ impl From<Protocol> for AuthenticatorVersion {
AuthenticatorVersion::V4
} else if value.version == v5::VERSION {
AuthenticatorVersion::V5
} else if value.version == v6::VERSION {
AuthenticatorVersion::V6
} else {
AuthenticatorVersion::UNKNOWN
}
@@ -72,6 +79,8 @@ impl From<u8> for AuthenticatorVersion {
AuthenticatorVersion::V4
} else if value == v5::VERSION {
AuthenticatorVersion::V5
} else if value == v6::VERSION {
AuthenticatorVersion::V6
} else {
AuthenticatorVersion::UNKNOWN
}
@@ -126,11 +135,14 @@ impl From<semver::Version> for AuthenticatorVersion {
if semver < AuthenticatorVersion::V5.release_version() {
return Self::V4;
}
// if provided version is higher (or equal) to release version of V5,
// we return the latest (i.e. v5)
if semver < AuthenticatorVersion::V6.release_version() {
return Self::V5;
}
// if provided version is higher (or equal) to release version of V6,
// we return the latest (i.e. v6)
debug_assert_eq!(
Self::V5,
Self::V6,
Self::LATEST,
"a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait"
);
@@ -191,5 +203,9 @@ mod tests {
assert_eq!(AuthenticatorVersion::V5, "1.7.0".into());
assert_eq!(AuthenticatorVersion::V5, "1.16.11".into());
assert_eq!(AuthenticatorVersion::V5, "1.17.0".into());
assert_eq!(AuthenticatorVersion::V5, "1.22.0".into());
assert_eq!(AuthenticatorVersion::V6, "1.23.0".into());
assert_eq!(AuthenticatorVersion::V6, "1.23.1".into());
assert_eq!(AuthenticatorVersion::V6, "1.24.0".into());
}
}
+1 -6
View File
@@ -7,21 +7,16 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = { workspace = true }
bip39 = { workspace = true }
async-trait = { workspace = true }
log = { workspace = true }
rand = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
zeroize = { workspace = true }
nym-credential-storage = { path = "../credential-storage" }
nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "stream_cipher", "aes", "hashing"] }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-time = { path = "../ecash-time" }
nym-network-defaults = { path = "../network-defaults" }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
+3
View File
@@ -21,6 +21,9 @@ pub enum BandwidthControllerError {
#[error("There was a credential storage error - {0}")]
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
#[error("retrieved upgrade mode token is not a valid String")]
MalformedUpgradeModeToken,
#[error("the credential storage does not contain any usable credentials")]
NoCredentialsAvailable,
+14 -1
View File
@@ -12,7 +12,7 @@ use crate::utils::{
ApiClientsWrapper,
};
use log::error;
use nym_credential_storage::models::RetrievedTicketbook;
use nym_credential_storage::models::{EmergencyCredential, RetrievedTicketbook};
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::CredentialSpendingData;
use nym_credentials_interface::{
@@ -220,6 +220,19 @@ impl<C, St: Storage> BandwidthController<C, St> {
}
}
}
pub async fn get_emergency_credential(
&self,
typ: &str,
) -> Result<Option<EmergencyCredential>, BandwidthControllerError>
where
<St as Storage>::StorageError: Send + Sync + 'static,
{
self.storage
.get_emergency_credential(typ)
.await
.map_err(BandwidthControllerError::credential_storage_error)
}
}
impl<C, St> Clone for BandwidthController<C, St>
+17
View File
@@ -11,6 +11,9 @@ use crate::{error::BandwidthControllerError, BandwidthController, PreparedCreden
pub const DEFAULT_TICKETS_TO_SPEND: u32 = 1;
// TODO: this does not really belong here
pub const UPGRADE_MODE_JWT_TYPE: &str = "UPGRADE_MODE_JWT";
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait BandwidthTicketProvider: Send + Sync {
@@ -20,6 +23,8 @@ pub trait BandwidthTicketProvider: Send + Sync {
gateway_id: ed25519::PublicKey,
tickets_to_spend: u32,
) -> Result<PreparedCredential, BandwidthControllerError>;
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError>;
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -39,4 +44,16 @@ where
self.prepare_ecash_ticket(ticket_type, gateway_id.to_bytes(), tickets_to_spend)
.await
}
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
let Some(emergency_credential) =
self.get_emergency_credential(UPGRADE_MODE_JWT_TYPE).await?
else {
return Ok(None);
};
// upgrade mode credential is just a simple stringified JWT
let token = String::from_utf8(emergency_credential.data.content)
.map_err(|_| BandwidthControllerError::MalformedUpgradeModeToken)?;
Ok(Some(token))
}
}
@@ -81,6 +81,10 @@ pub struct CommonClientInitArgs {
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub enabled_credentials_mode: Option<bool>,
/// Change the default minimum node performance used during initial node selection filtering.
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub minimum_gateway_performance: Option<u8>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[cfg_attr(feature = "cli", clap(long, hide = true))]
@@ -173,10 +177,14 @@ where
})?;
hardcoded_topology.entry_capable_nodes().cloned().collect()
} else {
let minimum_performance = common_args
.minimum_gateway_performance
.unwrap_or(core.debug.topology.minimum_gateway_performance);
crate::init::helpers::gateways_for_init(
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
minimum_performance,
core.debug.topology.ignore_ingress_epoch_role,
None,
)
+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,
@@ -30,7 +30,6 @@ pub(crate) async fn connect_async(
resolver
.resolve_str(domain)
.await?
.into_iter()
.map(|a| SocketAddr::new(a, port))
.collect()
}
@@ -88,3 +88,6 @@ features = ["js"]
[features]
wasm = []
[lints]
workspace = true
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use si_scale::helpers::bibytes2;
use std::fmt::{Display, Formatter};
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
use std::sync::Arc;
use std::time::Duration;
@@ -26,6 +27,39 @@ pub struct ClientBandwidth {
inner: Arc<ClientBandwidthInner>,
}
// simple helper for logging purposes to accommodate 'unknown' case
pub(crate) enum UpgradeModeEnabledWrapper {
True,
False,
Unknown,
}
impl From<Option<bool>> for UpgradeModeEnabledWrapper {
fn from(value: Option<bool>) -> Self {
match value {
Some(true) => UpgradeModeEnabledWrapper::True,
Some(false) => UpgradeModeEnabledWrapper::False,
None => UpgradeModeEnabledWrapper::Unknown,
}
}
}
impl From<bool> for UpgradeModeEnabledWrapper {
fn from(value: bool) -> Self {
Some(value).into()
}
}
impl Display for UpgradeModeEnabledWrapper {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
UpgradeModeEnabledWrapper::True => write!(f, "true"),
UpgradeModeEnabledWrapper::False => write!(f, "false"),
UpgradeModeEnabledWrapper::Unknown => write!(f, "unknown"),
}
}
}
struct ClientBandwidthInner {
/// the actual bandwidth amount (in bytes) available
available: AtomicI64,
@@ -71,26 +105,41 @@ impl ClientBandwidth {
self.inner.available.load(Ordering::Acquire)
}
pub(crate) fn maybe_log_bandwidth(&self, now: Option<OffsetDateTime>) {
pub(crate) fn maybe_log_bandwidth(
&self,
now: Option<OffsetDateTime>,
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
) {
let last = self.last_logged();
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
if last + Duration::from_secs(10) < now {
self.log_bandwidth(Some(now))
self.log_bandwidth(Some(now), upgrade_mode)
}
}
pub(crate) fn log_bandwidth(&self, now: Option<OffsetDateTime>) {
pub(crate) fn log_bandwidth(
&self,
now: Option<OffsetDateTime>,
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
) {
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
let upgrade_mode = upgrade_mode.into();
let remaining = self.remaining();
let remaining_bi2 = bibytes2(remaining as f64);
if remaining < 0 {
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
tracing::warn!(
"OUT OF BANDWIDTH. remaining: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
);
} else if remaining < 1_000_000 {
tracing::info!("remaining bandwidth: {remaining_bi2}");
tracing::info!(
"remaining bandwidth: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
);
} else {
tracing::debug!("remaining bandwidth: {remaining_bi2}");
tracing::trace!(
"remaining bandwidth: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
);
}
self.inner
@@ -98,26 +147,35 @@ impl ClientBandwidth {
.store(now.unix_timestamp(), Ordering::Relaxed)
}
pub(crate) fn update_and_maybe_log(&self, remaining: i64) {
pub(crate) fn update_and_maybe_log(
&self,
remaining: i64,
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
) {
let now = OffsetDateTime::now_utc();
self.inner.available.store(remaining, Ordering::Release);
self.inner
.last_updated_ts
.store(now.unix_timestamp(), Ordering::Relaxed);
self.maybe_log_bandwidth(Some(now))
self.maybe_log_bandwidth(Some(now), upgrade_mode)
}
pub(crate) fn update_and_log(&self, remaining: i64) {
pub(crate) fn update_and_log(
&self,
remaining: i64,
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
) {
let now = OffsetDateTime::now_utc();
self.inner.available.store(remaining, Ordering::Release);
self.inner
.last_updated_ts
.store(now.unix_timestamp(), Ordering::Relaxed);
self.log_bandwidth(Some(now))
self.log_bandwidth(Some(now), upgrade_mode)
}
fn last_logged(&self) -> OffsetDateTime {
// SAFETY: this value is always populated with valid timestamps
#[allow(clippy::unwrap_used)]
OffsetDateTime::from_unix_timestamp(self.inner.last_logged_ts.load(Ordering::Relaxed))
.unwrap()
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::GatewayClientError;
use nym_network_defaults::TicketTypeRepr::V1MixnetEntry;
use nym_credentials_interface::DEFAULT_MIXNET_REQUEST_BANDWIDTH_THRESHOLD;
use si_scale::helpers::bibytes2;
use std::time::Duration;
@@ -103,7 +103,7 @@ impl BandwidthTickets {
// 20% of entry ticket value
pub const DEFAULT_REMAINING_BANDWIDTH_THRESHOLD: i64 =
(V1MixnetEntry.bandwidth_value() / 5) as i64;
DEFAULT_MIXNET_REQUEST_BANDWIDTH_THRESHOLD;
pub const DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD: Option<i64> = None;
@@ -20,9 +20,9 @@ use nym_credentials_interface::TicketType;
use nym_crypto::asymmetric::ed25519;
use nym_gateway_requests::registration::handshake::client_handshake;
use nym_gateway_requests::{
BinaryRequest, ClientControlRequest, ClientRequest, GatewayProtocolVersionExt,
GatewayRequestsError, SensitiveServerResponse, ServerResponse, SharedGatewayKey,
SharedSymmetricKey, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION,
BandwidthResponse, BinaryRequest, ClientControlRequest, ClientRequest, GatewayProtocolVersion,
GatewayProtocolVersionExt, GatewayRequestsError, SensitiveServerResponse, ServerResponse,
SharedGatewayKey, SharedSymmetricKey, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
};
use nym_sphinx::forwarding::packet::MixPacket;
use nym_statistics_common::clients::connection::ConnectionStatsEvent;
@@ -101,8 +101,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
bandwidth_controller: Option<BandwidthController<C, St>>,
stats_reporter: ClientStatsSender,
// currently unused (but populated)
negotiated_protocol: Option<u8>,
negotiated_protocol: Option<GatewayProtocolVersion>,
// Callback on the fd as soon as the connection has been established
#[cfg(unix)]
@@ -166,10 +165,12 @@ impl<C, St> GatewayClient<C, St> {
}
#[cfg(not(target_arch = "wasm32"))]
#[allow(clippy::unreachable)]
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
SocketState::Available(mut socket) => Ok((*socket).close(None).await?),
SocketState::PartiallyDelegated(_) => {
// SAFETY: this is only called after the caller has already recovered the connection
unreachable!("this branch should have never been reached!")
}
_ => Ok(()), // no need to do anything in those cases
@@ -177,6 +178,7 @@ impl<C, St> GatewayClient<C, St> {
}
#[cfg(target_arch = "wasm32")]
#[allow(clippy::unreachable)]
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
SocketState::Available(socket) => {
@@ -184,6 +186,7 @@ impl<C, St> GatewayClient<C, St> {
Ok(())
}
SocketState::PartiallyDelegated(_) => {
// SAFETY: this is only called after the caller has already recovered the connection
unreachable!("this branch should have never been reached!")
}
_ => Ok(()), // no need to do anything in those cases
@@ -458,43 +461,16 @@ impl<C, St> GatewayClient<C, St> {
}
}
fn check_gateway_protocol(
&self,
gateway_protocol: Option<u8>,
) -> Result<(), GatewayClientError> {
debug!("gateway protocol: {gateway_protocol:?}, ours: {CURRENT_PROTOCOL_VERSION}");
// right now there are no failure cases here, but this might change in the future
match gateway_protocol {
None => {
warn!("the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0");
// note: in +1.2.0 we will have to return a hard error here
Ok(())
}
Some(v) if v > CURRENT_PROTOCOL_VERSION => {
let err = GatewayClientError::IncompatibleProtocol {
gateway: Some(v),
current: CURRENT_PROTOCOL_VERSION,
};
error!("{err}");
Err(err)
}
Some(_) => {
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
Ok(())
}
}
}
async fn register(
&mut self,
derive_aes256_gcm_siv_key: bool,
supported_gateway_protocol: Option<GatewayProtocolVersion>,
) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
let derive_aes256_gcm_siv_key = supported_gateway_protocol.supports_aes256_gcm_siv();
debug_assert!(self.connection.is_available());
log::debug!(
"registering with gateway. using legacy key derivation: {}",
@@ -505,14 +481,13 @@ impl<C, St> GatewayClient<C, St> {
// and putting it into the GatewayClient struct would be a hassle
let mut rng = OsRng;
let shared_key = match &mut self.connection {
let handshake_result = match &mut self.connection {
SocketState::Available(ws_stream) => client_handshake(
&mut rng,
ws_stream,
self.local_identity.as_ref(),
self.gateway_identity,
self.cfg.bandwidth.require_tickets,
derive_aes256_gcm_siv_key,
supported_gateway_protocol,
#[cfg(not(target_arch = "wasm32"))]
self.shutdown_token.clone(),
)
@@ -521,26 +496,31 @@ impl<C, St> GatewayClient<C, St> {
_ => return Err(GatewayClientError::ConnectionInInvalidState),
}?;
let (authentication_status, gateway_protocol) = match self.read_control_response().await? {
let authentication_status = match self.read_control_response().await? {
ServerResponse::Register {
protocol_version,
status,
} => (status, protocol_version),
upgrade_mode,
..
} => {
if upgrade_mode {
warn!("the system is currently undergoing an upgrade. some of its functionalities might be unstable")
}
status
}
ServerResponse::Error { message } => {
return Err(GatewayClientError::GatewayError(message))
}
other => return Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
};
self.check_gateway_protocol(gateway_protocol)?;
self.authenticated = authentication_status;
if self.authenticated {
self.shared_key = Some(Arc::new(shared_key));
self.shared_key = Some(Arc::new(handshake_result.derived_key));
}
// populate the negotiated protocol for future uses
self.negotiated_protocol = gateway_protocol;
self.negotiated_protocol = Some(handshake_result.negotiated_protocol);
Ok(())
}
@@ -623,13 +603,24 @@ impl<C, St> GatewayClient<C, St> {
protocol_version,
status,
bandwidth_remaining,
upgrade_mode,
} => {
self.check_gateway_protocol(protocol_version)?;
if protocol_version.is_future_version() {
// SAFETY: future version is always defined
#[allow(clippy::unwrap_used)]
let version = protocol_version.unwrap();
error!("the gateway insists on using v{version} protocol which is not supported by this client");
return Err(GatewayClientError::AuthenticationFailure);
}
self.authenticated = status;
self.bandwidth.update_and_maybe_log(bandwidth_remaining);
self.bandwidth
.update_and_maybe_log(bandwidth_remaining, upgrade_mode);
self.negotiated_protocol = protocol_version;
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
if upgrade_mode {
warn!("the system is currently undergoing an upgrade. some of its functionalities might be unstable")
}
Ok(())
}
@@ -650,7 +641,7 @@ impl<C, St> GatewayClient<C, St> {
.public_key()
.derive_destination_address();
let msg = ClientControlRequest::new_authenticate(
let msg = ClientControlRequest::new_legacy_authenticate(
self_address,
shared_key,
self.cfg.bandwidth.require_tickets,
@@ -659,25 +650,40 @@ impl<C, St> GatewayClient<C, St> {
.await
}
async fn authenticate_v2(&mut self) -> Result<(), GatewayClientError> {
async fn authenticate_v2(
&mut self,
requested_protocol_version: GatewayProtocolVersion,
) -> Result<(), GatewayClientError> {
debug!("using v2 authentication");
let Some(shared_key) = self.shared_key.as_ref() else {
return Err(GatewayClientError::NoSharedKeyAvailable);
};
let msg = ClientControlRequest::new_authenticate_v2(shared_key, &self.local_identity)?;
let msg = ClientControlRequest::new_authenticate_v2(
shared_key,
&self.local_identity,
requested_protocol_version,
)?;
self.send_authenticate_request_and_handle_response(msg)
.await
}
async fn authenticate(&mut self, use_v2: bool) -> Result<(), GatewayClientError> {
async fn authenticate(
&mut self,
supported_gateway_protocol: Option<GatewayProtocolVersion>,
) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
debug!("authenticating with gateway");
if use_v2 {
self.authenticate_v2().await
if supported_gateway_protocol.supports_authenticate_v2() {
// use the highest possible protocol version the gateway has announced support for
// SAFETY: if announced protocol supports auth v2, it means it's properly set
#[allow(clippy::unwrap_used)]
self.authenticate_v2(supported_gateway_protocol.unwrap())
.await
} else {
self.authenticate_v1().await
}
@@ -708,9 +714,12 @@ impl<C, St> GatewayClient<C, St> {
}
};
debug!("supported gateway protocol: {gw_protocol:?}");
let supports_aes_gcm_siv = gw_protocol.supports_aes256_gcm_siv();
let supports_auth_v2 = gw_protocol.supports_authenticate_v2();
let supports_key_rotation_info = gw_protocol.supports_key_rotation_packet();
let supports_upgrade_mode = gw_protocol.supports_upgrade_mode();
if !supports_aes_gcm_siv {
warn!("this gateway is on an old version that doesn't support AES256-GCM-SIV");
@@ -721,6 +730,16 @@ impl<C, St> GatewayClient<C, St> {
if !supports_key_rotation_info {
warn!("this gateway is on an old version that doesn't support key rotation packets")
}
if !supports_upgrade_mode {
warn!("this gateway is on an old version that doesn't support upgrade mode")
}
let gw_protocol = if gw_protocol.is_future_version() {
warn!("we're running outdated software as gateway is announcing protocol {gw_protocol:?} whilst we're using {}. we're going to attempt to downgrade", GatewayProtocolVersion::CURRENT);
Some(GatewayProtocolVersion::CURRENT)
} else {
gw_protocol
};
if self.authenticated {
debug!("Already authenticated");
@@ -735,10 +754,11 @@ impl<C, St> GatewayClient<C, St> {
}
if self.shared_key.is_some() {
self.authenticate(supports_auth_v2).await?;
self.authenticate(gw_protocol).await?;
if self.authenticated {
// if we are authenticated it means we MUST have an associated shared_key
#[allow(clippy::unwrap_used)]
let shared_key = self.shared_key.as_ref().unwrap();
let requires_key_upgrade = shared_key.is_legacy() && supports_aes_gcm_siv;
@@ -751,9 +771,10 @@ impl<C, St> GatewayClient<C, St> {
Err(GatewayClientError::AuthenticationFailure)
}
} else {
self.register(supports_aes_gcm_siv).await?;
self.register(gw_protocol).await?;
// if registration didn't return an error, we MUST have an associated shared key
#[allow(clippy::unwrap_used)]
let shared_key = self.shared_key.as_ref().unwrap();
// we're always registering with the highest supported protocol,
@@ -783,51 +804,81 @@ impl<C, St> GatewayClient<C, St> {
}
}
async fn claim_ecash_bandwidth(
async fn wait_for_bandwidth_response(
&mut self,
credential: CredentialSpendingData,
) -> Result<(), GatewayClientError> {
let msg = ClientControlRequest::new_enc_ecash_credential(
credential,
self.shared_key.as_ref().unwrap(),
)?;
let bandwidth_remaining = match self
msg: ClientControlRequest,
) -> Result<BandwidthResponse, GatewayClientError> {
let response = match self
.send_websocket_message_with_non_send_response(msg)
.await?
{
ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Bandwidth(response) => {
if response.upgrade_mode {
info!("the system is currently undergoing an upgrade. our bandwidth shouldn't have been metered")
}
Ok(response)
}
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
ServerResponse::TypedError { error } => {
Err(GatewayClientError::TypedGatewayError(error))
}
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
}?;
Ok(response)
}
async fn claim_ecash_bandwidth(
&mut self,
credential: CredentialSpendingData,
) -> Result<(), GatewayClientError> {
// SAFETY: claiming ecash bandwidth is called as part of `claim_bandwidth` which
// ensures the shared key is defined
#[allow(clippy::unwrap_used)]
let msg = ClientControlRequest::new_enc_ecash_credential(
credential,
self.shared_key.as_ref().unwrap(),
)?;
let response = self.wait_for_bandwidth_response(msg).await?;
// TODO: create tracing span
info!("managed to claim ecash bandwidth");
self.bandwidth.update_and_log(bandwidth_remaining);
self.bandwidth
.update_and_log(response.available_total, response.upgrade_mode);
Ok(())
}
pub async fn send_upgrade_mode_jwt(&mut self, token: String) -> Result<(), GatewayClientError> {
let msg = ClientControlRequest::new_upgrade_mode_jwt(token);
let response = self.wait_for_bandwidth_response(msg).await?;
// if gateway rejected our jwt, we would have returned an error
info!("gateway has accepted our jwt");
if !response.upgrade_mode {
error!("but we're not in upgrade mode - something is wrong!");
return Err(GatewayClientError::UnexpectedUpgradeModeState);
}
self.bandwidth
.update_and_log(response.available_total, response.upgrade_mode);
Ok(())
}
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth;
let bandwidth_remaining = match self
.send_websocket_message_with_non_send_response(msg)
.await?
{
ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
}?;
let response = self.wait_for_bandwidth_response(msg).await?;
info!("managed to claim testnet bandwidth");
self.bandwidth.update_and_log(bandwidth_remaining);
self.bandwidth
.update_and_log(response.available_total, response.upgrade_mode);
Ok(())
}
fn unchecked_bandwidth_controller(&self) -> &BandwidthController<C, St> {
// this is an unchecked method
#[allow(clippy::unwrap_used)]
self.bandwidth_controller.as_ref().unwrap()
}
@@ -919,6 +970,7 @@ impl<C, St> GatewayClient<C, St> {
BinaryRequest::ForwardSphinx { packet }
};
#[allow(clippy::expect_used)]
req.into_ws_message(
self.shared_key
.as_ref()
@@ -1025,6 +1077,8 @@ impl<C, St> GatewayClient<C, St> {
self.send_with_reconnection_on_failure(msg).await
}
// SAFETY: this method is only called when the connection is in `PartiallyDelegated` state
#[allow(clippy::unreachable)]
async fn recover_socket_connection(&mut self) -> Result<(), GatewayClientError> {
if self.connection.is_available() {
return Ok(());
@@ -1054,6 +1108,7 @@ impl<C, St> GatewayClient<C, St> {
return Err(GatewayClientError::ConnectionInInvalidState);
}
#[allow(clippy::expect_used)]
let partially_delegated =
match std::mem::replace(&mut self.connection, SocketState::Invalid) {
SocketState::Available(conn) => {
@@ -1069,7 +1124,13 @@ impl<C, St> GatewayClient<C, St> {
self.shutdown_token.clone(),
)
}
_ => unreachable!(),
other => {
error!(
"attempted to start mixnet listener whilst the connection is in {} state!",
other.name()
);
return Err(GatewayClientError::ConnectionInInvalidState);
}
};
self.connection = SocketState::PartiallyDelegated(partially_delegated);
@@ -1082,8 +1143,7 @@ impl<C, St> GatewayClient<C, St> {
}
// if we're reconnecting, because we lost connection, we need to re-authenticate the connection
self.authenticate(self.negotiated_protocol.supports_authenticate_v2())
.await?;
self.authenticate(self.negotiated_protocol).await?;
// this call is NON-blocking
self.start_listening_for_mixnet_messages()?;
@@ -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,
@@ -128,6 +128,9 @@ pub enum GatewayClientError {
"this operation couldn't be completed as the program is in the process of shutting down"
)]
ShutdownInProgress,
#[error("the system is an unexpected upgrade mode state")]
UnexpectedUpgradeModeState,
}
impl From<WsError> for GatewayClientError {
@@ -35,6 +35,7 @@ impl PacketRouter {
}
}
#[allow(clippy::panic)]
pub fn route_mixnet_messages(
&self,
received_messages: Vec<Vec<u8>>,
@@ -54,6 +55,7 @@ impl PacketRouter {
Ok(())
}
#[allow(clippy::panic)]
pub fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), GatewayClientError> {
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
// check if the failure is due to the shutdown being in progress and thus the receiver channel
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::bandwidth::ClientBandwidth;
use crate::client::config::BandwidthTickets;
use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter;
use crate::traits::GatewayPacketRouter;
@@ -10,7 +11,9 @@ use futures::channel::oneshot;
use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use nym_gateway_requests::shared_key::SharedGatewayKey;
use nym_gateway_requests::{SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError};
use nym_gateway_requests::{
SendResponse, SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError,
};
use nym_task::ShutdownToken;
use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd;
@@ -154,11 +157,12 @@ impl PartiallyDelegatedRouter {
fn handle_text_message(&self, text: String) -> Result<(), GatewayClientError> {
// if we fail to deserialise the response, return a hard error. we can't handle garbage
match ServerResponse::try_from(text).map_err(|_| GatewayClientError::MalformedResponse)? {
ServerResponse::Send {
ServerResponse::Send(SendResponse {
remaining_bandwidth,
} => {
upgrade_mode,
}) => {
self.client_bandwidth
.update_and_maybe_log(remaining_bandwidth);
.update_and_maybe_log(remaining_bandwidth, upgrade_mode);
Ok(())
}
ServerResponse::Error { message } => {
@@ -174,7 +178,20 @@ impl PartiallyDelegatedRouter {
let available_bi2 = bibytes2(available as f64);
let required_bi2 = bibytes2(required as f64);
warn!("run out of bandwidth when attempting to send the message! we got {available_bi2} available, but needed at least {required_bi2} to send the previous message");
self.client_bandwidth.update_and_log(available);
// if we run out of bandwidth (and tried to send reasonable amount of data),
// the upgrade mode is implicitly disabled, as otherwise we would have been
// to proceed
let upgrade_mode = if available
< BandwidthTickets::DEFAULT_REMAINING_BANDWIDTH_THRESHOLD
{
Some(false)
} else {
// we were attempting to send a lot of data at once
// - we have no certainty about upgrade mode at this point
None
};
self.client_bandwidth
.update_and_log(available, upgrade_mode);
// UNIMPLEMENTED: we should stop sending messages until we recover bandwidth
Ok(())
}
@@ -327,6 +344,7 @@ impl PartiallyDelegatedHandle {
Ok(self.sink_half.send_all(&mut send_stream).await?)
}
#[allow(clippy::panic)]
pub(crate) async fn merge(self) -> Result<WsConn, GatewayClientError> {
let (mut stream_receiver, notify) = self.delegated_stream;
@@ -355,8 +373,10 @@ impl PartiallyDelegatedHandle {
// in receive_res
.map_err(|_| GatewayClientError::ConnectionAbruptlyClosed)?;
let stream = stream_results?;
// the error is thrown when trying to reunite sink and stream that did not originate
// from the same split which is impossible to happen here
#[allow(clippy::unwrap_used)]
Ok(self.sink_half.reunite(stream).unwrap())
}
}
@@ -387,4 +407,13 @@ impl SocketState {
SocketState::Available(_) | SocketState::PartiallyDelegated(_)
)
}
pub(crate) fn name(&self) -> &'static str {
match self {
SocketState::Available(_) => "available",
SocketState::PartiallyDelegated(_) => "partially delegated",
SocketState::NotConnected => "not connected",
SocketState::Invalid => "invalid",
}
}
}
@@ -75,6 +75,7 @@ workspace = true
features = ["json", "rustls-tls"]
[dev-dependencies]
anyhow = { workspace = true }
bip39 = { workspace = true }
cosmrs = { workspace = true, features = ["bip32"] }
ts-rs = { workspace = true }
@@ -7,6 +7,7 @@ use cosmrs::{tx, AccountId, Coin, Denom};
use nym_validator_client::http_client;
use nym_validator_client::nyxd::CosmWasmClient;
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
use nym_validator_client::signing::signer::OfflineSigner;
use nym_validator_client::signing::tx_signer::TxSigner;
use nym_validator_client::signing::SignerData;
@@ -19,8 +20,8 @@ async fn main() {
let validator = "https://rpc.sandbox.nymtech.net";
let to_address: AccountId = "n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa".parse().unwrap();
let signer = DirectSecp256k1HdWallet::from_mnemonic(prefix, signer_mnemonic);
let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone();
let signer = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, signer_mnemonic).unwrap();
let signer_address = signer.signer_addresses()[0].clone();
// local 'client' ONLY signing messages
let tx_signer = signer;
@@ -57,9 +58,15 @@ async fn main() {
100000u32,
);
let tx_raw = tx_signer
.sign_direct(&signer_address, vec![send_msg], fee, memo, signer_data)
.unwrap();
let tx_raw = TxSigner::sign_direct(
&tx_signer,
&signer_address,
vec![send_msg],
fee,
memo,
signer_data,
)
.unwrap();
let tx_bytes = tx_raw.to_bytes().unwrap();
// compare balances from before and after the tx
@@ -5,8 +5,7 @@ use crate::nyxd::{self, NyxdClient};
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
use crate::signing::signer::{NoSigner, OfflineSigner};
use crate::{
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ReqwestRpcClient,
ValidatorClientError,
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ValidatorClientError,
};
use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
@@ -164,7 +163,7 @@ impl Client<HttpRpcClient, DirectSecp256k1HdWallet> {
) -> Result<DirectSigningHttpRpcValidatorClient, ValidatorClientError> {
let rpc_client = http_client(config.nyxd_url.as_str())?;
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
let wallet = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic)?;
Ok(Self::new_signing_with_rpc_client(
config, rpc_client, wallet,
@@ -177,12 +176,13 @@ impl Client<HttpRpcClient, DirectSecp256k1HdWallet> {
}
}
impl Client<ReqwestRpcClient, DirectSecp256k1HdWallet> {
#[allow(deprecated)]
impl Client<crate::ReqwestRpcClient, DirectSecp256k1HdWallet> {
pub fn new_reqwest_signing(
config: Config,
mnemonic: bip39::Mnemonic,
) -> DirectSigningReqwestRpcValidatorClient {
let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone());
let rpc_client = crate::ReqwestRpcClient::new(config.nyxd_url.clone());
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
@@ -203,9 +203,10 @@ impl Client<HttpRpcClient> {
}
}
impl Client<ReqwestRpcClient> {
#[allow(deprecated)]
impl Client<crate::ReqwestRpcClient> {
pub fn new_reqwest_query(config: Config) -> QueryReqwestRpcValidatorClient {
let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone());
let rpc_client = crate::ReqwestRpcClient::new(config.nyxd_url.clone());
Self::new_with_rpc_client(config, rpc_client)
}
}
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::nym_api;
use crate::signing::direct_wallet::DirectSecp256k1HdWalletError;
pub use tendermint_rpc::error::Error as TendermintRpcError;
use thiserror::Error;
@@ -26,6 +27,12 @@ pub enum ValidatorClientError {
#[error("No validator API url has been provided")]
NoAPIUrlAvailable,
#[error("failed to derive signing accounts: {source}")]
AccountDerivationFailure {
#[from]
source: DirectSecp256k1HdWalletError,
},
}
impl From<nym_api::error::NymAPIError> for ValidatorClientError {
@@ -12,6 +12,7 @@ pub mod rpc;
pub mod signing;
pub use crate::error::ValidatorClientError;
#[allow(deprecated)]
pub use crate::rpc::reqwest::ReqwestRpcClient;
pub use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
pub use client::{Client, Config, EcashApiClient};
@@ -38,9 +39,13 @@ pub type DirectSigningHttpRpcValidatorClient = Client<HttpRpcClient, DirectSecp2
#[cfg(feature = "http-client")]
pub type DirectSigningHttpRpcNyxdClient = nyxd::NyxdClient<HttpRpcClient, DirectSecp256k1HdWallet>;
#[allow(deprecated)]
pub type QueryReqwestRpcValidatorClient = Client<ReqwestRpcClient>;
#[allow(deprecated)]
pub type QueryReqwestRpcNyxdClient = nyxd::NyxdClient<ReqwestRpcClient>;
#[allow(deprecated)]
pub type DirectSigningReqwestRpcValidatorClient = Client<ReqwestRpcClient, DirectSecp256k1HdWallet>;
#[allow(deprecated)]
pub type DirectSigningReqwestRpcNyxdClient =
nyxd::NyxdClient<ReqwestRpcClient, DirectSecp256k1HdWallet>;
@@ -178,7 +178,7 @@ where
.ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?;
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let signer_address = &self.signer_addresses()?[0];
let signer_address = &self.signer_addresses()[0];
self.execute(signer_address, dkg_contract_address, &msg, fee, memo, funds)
.await
@@ -99,7 +99,7 @@ where
.ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?;
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let signer_address = &self.signer_addresses()?[0];
let signer_address = &self.signer_addresses()[0];
self.execute(
signer_address,
@@ -95,7 +95,7 @@ where
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let signer_address = &self.signer_addresses()?[0];
let signer_address = &self.signer_addresses()[0];
self.execute(
signer_address,
group_contract_address,
@@ -667,7 +667,7 @@ where
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let memo = msg.default_memo();
let signer_address = &self.signer_addresses()?[0];
let signer_address = &self.signer_addresses()[0];
self.execute(
signer_address,
mixnet_contract_address,
@@ -133,7 +133,7 @@ where
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let signer_address = &self.signer_addresses()?[0];
let signer_address = &self.signer_addresses()[0];
self.execute(
signer_address,
multisig_contract_address,
@@ -165,7 +165,7 @@ where
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let signer_address = &self.signer_addresses()?[0];
let signer_address = &self.signer_addresses()[0];
self.execute(
signer_address,
performance_contract_address,
@@ -375,7 +375,7 @@ where
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let memo = msg.name().to_string();
let signer_address = &self.signer_addresses()?[0];
let signer_address = &self.signer_addresses()[0];
self.execute(
signer_address,
vesting_contract_address,
@@ -324,7 +324,7 @@ where
{
type Error = S::Error;
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
fn get_accounts(&self) -> &[AccountData] {
self.signer.get_accounts()
}
@@ -19,7 +19,7 @@ use crate::signing::signer::NoSigner;
use crate::signing::signer::OfflineSigner;
use crate::signing::tx_signer::TxSigner;
use crate::signing::AccountData;
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient};
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient};
use async_trait::async_trait;
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
use cosmrs::tx::{Raw, SignDoc};
@@ -158,12 +158,13 @@ impl NyxdClient<HttpClient> {
}
}
impl NyxdClient<ReqwestRpcClient> {
#[allow(deprecated)]
impl NyxdClient<crate::ReqwestRpcClient> {
pub fn connect_reqwest(
config: Config,
endpoint: Url,
) -> Result<QueryReqwestRpcNyxdClient, NyxdError> {
let client = ReqwestRpcClient::new(endpoint);
let client = crate::ReqwestRpcClient::new(endpoint);
Ok(NyxdClient {
client: MaybeSigningClient::new(client, (&config).into()),
@@ -195,18 +196,19 @@ impl NyxdClient<HttpClient, DirectSecp256k1HdWallet> {
let client = http_client(endpoint)?;
let prefix = &config.chain_details.bech32_account_prefix;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
let wallet = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic)?;
Ok(Self::connect_with_signer(config, client, wallet))
}
}
impl NyxdClient<ReqwestRpcClient, DirectSecp256k1HdWallet> {
#[allow(deprecated)]
impl NyxdClient<crate::ReqwestRpcClient, DirectSecp256k1HdWallet> {
pub fn connect_reqwest_with_mnemonic(
config: Config,
endpoint: Url,
mnemonic: bip39::Mnemonic,
) -> DirectSigningReqwestRpcNyxdClient {
let client = ReqwestRpcClient::new(endpoint);
let client = crate::ReqwestRpcClient::new(endpoint);
let prefix = &config.chain_details.bech32_account_prefix;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
@@ -391,17 +393,12 @@ where
S: OfflineSigner + Send + Sync,
NyxdError: From<<S as OfflineSigner>::Error>,
{
pub fn signing_account(&self) -> Result<AccountData, NyxdError> {
pub fn signing_account(&self) -> Result<&AccountData, NyxdError> {
Ok(self.find_account(&self.address())?)
}
pub fn address(&self) -> AccountId {
match self.client.signer_addresses() {
Ok(addresses) => addresses[0].clone(),
Err(_) => {
panic!("key derivation failure")
}
}
self.client.signer_addresses()[0].clone()
}
pub fn mix_coin(&self, amount: u128) -> Coin {
@@ -867,7 +864,7 @@ where
{
type Error = S::Error;
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
fn get_accounts(&self) -> &[AccountData] {
self.client.get_accounts()
}
@@ -42,12 +42,15 @@ macro_rules! perform_with_compat {
}};
}
// the separate implementation is now completely redundant
#[deprecated(note = "use HttpClient directly instead")]
pub struct ReqwestRpcClient {
compat: CompatMode,
inner: reqwest::Client,
url: Url,
}
#[allow(deprecated)]
impl ReqwestRpcClient {
pub fn new(url: Url) -> Self {
ReqwestRpcClient {
@@ -131,6 +134,7 @@ impl TendermintRpcErrorMap for reqwest::Error {
}
}
#[allow(deprecated)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl TendermintRpcClient for ReqwestRpcClient {
@@ -2,18 +2,18 @@
// SPDX-License-Identifier: Apache-2.0
use crate::signing::signer::{OfflineSigner, SigningError};
use crate::signing::{AccountData, Secp256k1Derivation};
use cosmrs::bip32::{DerivationPath, XPrv};
use cosmrs::crypto::secp256k1::SigningKey;
use cosmrs::crypto::PublicKey;
use crate::signing::{
derive_extended_private_key, derive_keypair, AccountData, Secp256k1Derivation, Secp256k1Keypair,
};
use bip32::XPrv;
use cosmrs::bip32::DerivationPath;
use cosmrs::tx;
use cosmrs::tx::SignDoc;
use nym_config::defaults;
use std::borrow::Cow;
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
type Secp256k1Keypair = (SigningKey, PublicKey);
#[derive(Debug, Error)]
pub enum DirectSecp256k1HdWalletError {
#[error(transparent)]
@@ -36,28 +36,22 @@ pub enum DirectSecp256k1HdWalletError {
}
// TODO: maybe lock this one behind feature flag?
#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)]
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct DirectSecp256k1HdWallet {
/// Base secret
secret: bip39::Mnemonic,
/// BIP39 seed
seed: [u8; 64],
// An unfortunate result of immature rust async story is that async traits (only available in the separate package)
// can't yet figure out everything and if we stored our derived account data on the struct,
// that would include the secret key which is a dyn EcdsaSigner and hence not Sync making the wallet
// not Sync and if used on the signing client in an async trait, it wouldn't be Send
/// Derivation instructions
/// Derived accounts
#[zeroize(skip)]
accounts: Vec<Secp256k1Derivation>,
// unfortunately `dyn EcdsaSigner` does not guarantee Zeroize
accounts: Vec<AccountData>,
}
impl OfflineSigner for DirectSecp256k1HdWallet {
type Error = DirectSecp256k1HdWalletError;
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
self.try_derive_accounts()
fn get_accounts(&self) -> &[AccountData] {
&self.accounts
}
fn sign_direct_with_account(
@@ -77,55 +71,27 @@ impl DirectSecp256k1HdWallet {
}
/// Restores a wallet from the given BIP39 mnemonic using default options.
#[deprecated(
note = "this function can potentially panic if accounts can't be derived correctly. please use .checked_from_mnemonic() instead"
)]
pub fn from_mnemonic(prefix: &str, mnemonic: bip39::Mnemonic) -> Self {
// unfortunately due to backwards compatibility requirements,
// we can't change signature of this method
#[allow(deprecated)]
DirectSecp256k1HdWalletBuilder::new(prefix).build(mnemonic)
}
/// Restores a wallet from the given BIP39 mnemonic using default options.
pub fn checked_from_mnemonic(
prefix: &str,
mnemonic: bip39::Mnemonic,
) -> Result<Self, DirectSecp256k1HdWalletError> {
DirectSecp256k1HdWalletBuilder::new(prefix).try_build(mnemonic)
}
pub fn generate(prefix: &str, word_count: usize) -> Result<Self, DirectSecp256k1HdWalletError> {
let mneomonic = bip39::Mnemonic::generate(word_count)?;
Ok(Self::from_mnemonic(prefix, mneomonic))
}
fn derive_keypair(
&self,
hd_path: &DerivationPath,
) -> Result<Secp256k1Keypair, DirectSecp256k1HdWalletError> {
let extended_private_key = XPrv::derive_from_path(self.seed, hd_path)?;
let private_key: SigningKey = extended_private_key.into();
let public_key = private_key.public_key();
Ok((private_key, public_key))
}
pub fn derive_extended_private_key(
&self,
hd_path: &DerivationPath,
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
Ok(XPrv::derive_from_path(self.seed, hd_path)?)
}
pub fn try_derive_accounts(&self) -> Result<Vec<AccountData>, DirectSecp256k1HdWalletError> {
let mut accounts = Vec::with_capacity(self.accounts.len());
for derivation_info in &self.accounts {
let keypair = self.derive_keypair(&derivation_info.hd_path)?;
// it seems this can only fail if the provided account prefix is invalid
let address = keypair
.1
.account_id(&derivation_info.prefix)
.map_err(
|source| DirectSecp256k1HdWalletError::AccountDerivationError { source },
)?;
accounts.push(AccountData {
address,
public_key: keypair.1,
private_key: keypair.0,
})
}
Ok(accounts)
Self::checked_from_mnemonic(prefix, mneomonic)
}
pub fn secret(&self) -> &bip39::Mnemonic {
@@ -142,6 +108,43 @@ impl DirectSecp256k1HdWallet {
pub fn mnemonic_string(&self) -> Zeroizing<String> {
Zeroizing::new(self.secret.to_string())
}
pub fn account_seed<'a, P: Into<Cow<'a, str>>>(
&self,
bip39_password: P,
) -> Zeroizing<[u8; 64]> {
Zeroizing::new(self.secret.to_seed(bip39_password))
}
/// Derive an extended private key from the stored account secret assuming no bip39 password
#[deprecated(
note = "use derive_extended_private_key_with_password to ensure correct derivation if used bip39 password"
)]
pub fn derive_extended_private_key(
&self,
hd_path: &DerivationPath,
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
let seed = self.account_seed("");
derive_extended_private_key(seed, hd_path)
}
pub fn derive_keypair<'a, P: Into<Cow<'a, str>>>(
&self,
hd_path: &DerivationPath,
bip39_password: P,
) -> Result<Secp256k1Keypair, DirectSecp256k1HdWalletError> {
let seed = self.account_seed(bip39_password);
derive_keypair(seed, hd_path)
}
pub fn derive_extended_private_key_with_password<'a, P: Into<Cow<'a, str>>>(
&self,
hd_path: &DerivationPath,
bip39_password: P,
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
let seed = self.account_seed(bip39_password);
derive_extended_private_key(seed, hd_path)
}
}
#[must_use]
@@ -188,23 +191,39 @@ impl DirectSecp256k1HdWalletBuilder {
self
}
#[deprecated(
note = "this function can potentially panic if accounts can't be derived correctly. please use .try_build() instead"
)]
pub fn build(self, mnemonic: bip39::Mnemonic) -> DirectSecp256k1HdWallet {
let seed = mnemonic.to_seed(&self.bip39_password);
// unfortunately due to backwards compatibility requirements,
// we can't change signature of this method
#[allow(clippy::expect_used)]
self.try_build(mnemonic)
.expect("account derivation failure")
}
pub fn try_build(
self,
mnemonic: bip39::Mnemonic,
) -> Result<DirectSecp256k1HdWallet, DirectSecp256k1HdWalletError> {
let seed = Zeroizing::new(mnemonic.to_seed(&self.bip39_password));
let prefix = self.prefix.clone();
let accounts = self
.hd_paths
.iter()
.map(|hd_path| Secp256k1Derivation {
hd_path: hd_path.clone(),
prefix: prefix.clone(),
.map(|hd_path| {
Secp256k1Derivation {
hd_path: hd_path.clone(),
prefix: prefix.clone(),
}
.try_derive_account(&seed)
})
.collect();
.collect::<Result<_, _>>()?;
DirectSecp256k1HdWallet {
Ok(DirectSecp256k1HdWallet {
accounts,
seed,
secret: mnemonic,
}
})
}
}
@@ -215,7 +234,7 @@ mod tests {
use super::*;
#[test]
fn generating_account_addresses() {
fn generating_account_addresses() -> anyhow::Result<()> {
// test vectors produced from our js wallet
let mnemonics = ["crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove",
"acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel",
@@ -230,11 +249,10 @@ mod tests {
"n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf",
];
for (idx, mnemonic) in mnemonics.iter().enumerate() {
let wallet = DirectSecp256k1HdWallet::from_mnemonic(&prefix, mnemonic.parse().unwrap());
assert_eq!(
wallet.try_derive_accounts().unwrap()[0].address,
addrs[idx].parse().unwrap()
)
let wallet =
DirectSecp256k1HdWallet::checked_from_mnemonic(&prefix, mnemonic.parse()?)?;
assert_eq!(wallet.signer_addresses()[0], addrs[idx].parse().unwrap());
}
Ok(())
}
}
@@ -1,6 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::signing::direct_wallet::DirectSecp256k1HdWalletError;
use bip32::XPrv;
use cosmrs::bip32::DerivationPath;
use cosmrs::crypto::secp256k1::SigningKey;
use cosmrs::crypto::PublicKey;
@@ -12,14 +14,64 @@ pub mod direct_wallet;
pub mod signer;
pub mod tx_signer;
pub(crate) type Secp256k1Keypair = (SigningKey, PublicKey);
/// Derivation information required to derive a keypair and an address from a mnemonic.
#[derive(Debug, Clone)]
struct Secp256k1Derivation {
pub(crate) struct Secp256k1Derivation {
hd_path: DerivationPath,
prefix: String,
}
// TODO: is this struct going to be derivable with other signer types?
impl Secp256k1Derivation {
pub(crate) fn try_derive_account<S>(
&self,
seed: S,
) -> Result<AccountData, DirectSecp256k1HdWalletError>
where
S: AsRef<[u8]>,
{
let keypair = derive_keypair(seed, &self.hd_path)?;
// it seems this can only fail if the provided account prefix is invalid
let address = keypair
.1
.account_id(&self.prefix)
.map_err(|source| DirectSecp256k1HdWalletError::AccountDerivationError { source })?;
Ok(AccountData {
address,
public_key: keypair.1,
private_key: keypair.0,
})
}
}
pub fn derive_keypair<S>(
seed: S,
hd_path: &DerivationPath,
) -> Result<Secp256k1Keypair, DirectSecp256k1HdWalletError>
where
S: AsRef<[u8]>,
{
let extended_private_key = derive_extended_private_key(seed, hd_path)?;
let private_key: SigningKey = extended_private_key.into();
let public_key = private_key.public_key();
Ok((private_key, public_key))
}
pub fn derive_extended_private_key<S>(
seed: S,
hd_path: &DerivationPath,
) -> Result<XPrv, DirectSecp256k1HdWalletError>
where
S: AsRef<[u8]>,
{
Ok(XPrv::derive_from_path(seed, hd_path)?)
}
pub struct AccountData {
pub address: AccountId,
@@ -33,23 +33,18 @@ pub enum SignerType {
pub trait OfflineSigner {
type Error: From<SigningError>;
// I really dislike existence of this function because it makes you re-derive your key **twice** for each contract transaction
fn signer_addresses(&self) -> Result<Vec<AccountId>, Self::Error> {
let derived_addresses = self
.get_accounts()?
.into_iter()
.map(|account| account.address)
.collect();
Ok(derived_addresses)
fn signer_addresses(&self) -> Vec<AccountId> {
self.get_accounts()
.iter()
.map(|account| account.address.clone())
.collect()
}
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error>;
fn get_accounts(&self) -> &[AccountData];
fn find_account(&self, signer_address: &AccountId) -> Result<AccountData, Self::Error> {
// TODO: we could really use some zeroize action here
let accounts = self.get_accounts()?;
accounts
.into_iter()
fn find_account(&self, signer_address: &AccountId) -> Result<&AccountData, Self::Error> {
self.get_accounts()
.iter()
.find(|account| &account.address == signer_address)
.ok_or_else(|| {
SigningError::AccountNotFound {
@@ -76,7 +71,7 @@ pub trait OfflineSigner {
message: M,
) -> Result<Signature, Self::Error> {
let signer = self.find_account(signer_address)?;
self.sign_raw_with_account(&signer, message)
self.sign_raw_with_account(signer, message)
}
fn sign_direct(
@@ -85,7 +80,7 @@ pub trait OfflineSigner {
sign_doc: SignDoc,
) -> Result<tx::Raw, Self::Error> {
let signer = self.find_account(signer_address)?;
self.sign_direct_with_account(&signer, sign_doc)
self.sign_direct_with_account(signer, sign_doc)
}
// unless explicitly defined, each signing method is unsupported
@@ -122,7 +117,7 @@ pub struct NoSigner;
// impl OfflineSigner for NoSigner {
// type Error = SignerUnavailable;
//
// fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
// fn get_accounts(&self) -> &[AccountData] {
// return Err(SignerUnavailable);
// }
// }
@@ -50,7 +50,7 @@ pub trait TxSigner: OfflineSigner {
)
.map_err(|source| SigningError::SignDocFailure { source })?;
self.sign_direct_with_account(&account_from_signer, sign_doc)
self.sign_direct_with_account(account_from_signer, sign_doc)
}
}
@@ -3,6 +3,7 @@
use clap::Parser;
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
use nym_validator_client::signing::signer::OfflineSigner;
#[derive(Debug, Parser)]
pub struct Args {
@@ -15,9 +16,10 @@ pub fn create_account(args: Args, prefix: &str) {
let word_count = args.word_count.unwrap_or(24);
let mnemonic = bip39::Mnemonic::generate(word_count).expect("failed to generate mnemonic!");
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
let wallet = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic)
.expect("failed to derive accounts!");
// Output address and mnemonics into separate lines for easier parsing
println!("{}", wallet.mnemonic_string().as_str());
println!("{}", wallet.try_derive_accounts().unwrap()[0].address());
println!("{}", wallet.signer_addresses()[0]);
}
+15 -16
View File
@@ -1,13 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClient;
use crate::utils::show_error;
use clap::Parser;
use log::{error, info};
use nym_validator_client::nyxd::{AccountId, CosmWasmClient};
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
use crate::context::QueryClient;
use crate::utils::show_error;
use nym_validator_client::signing::signer::OfflineSigner;
#[derive(Debug, Parser)]
pub struct Args {
@@ -50,20 +50,19 @@ pub async fn get_pubkey(
}
pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip39::Mnemonic) {
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
match wallet.try_derive_accounts() {
Ok(accounts) => match accounts.iter().find(|a| *a.address() == address) {
Some(account) => {
println!("{}", account.public_key().to_string());
}
None => {
error!("Could not derive key that matches {address}")
}
},
Err(e) => {
error!("Failed to derive accounts. {e}");
let wallet = match DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic) {
Ok(wallet) => wallet,
Err(err) => {
error!("Failed to derive accounts. {err}");
return;
}
}
};
let Ok(account) = wallet.find_account(&address) else {
error!("Could not derive key that matches {address}");
return;
};
println!("{}", account.public_key().to_string());
}
pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) {
+27 -24
View File
@@ -36,32 +36,35 @@ pub fn sign(args: Args, prefix: &str, mnemonic: Option<bip39::Mnemonic>) {
return;
}
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.expect("mnemonic not set"));
match wallet.try_derive_accounts() {
Ok(accounts) => match accounts.first() {
Some(account) => {
let msg = args.message.into_bytes();
match wallet.sign_raw_with_account(account, msg) {
Ok(signature) => {
let output = SignatureOutputJson {
account_id: account.address().to_string(),
public_key: account.public_key(),
signature_as_hex: signature.to_string(),
};
println!("{}", json!(output));
}
Err(e) => {
error!("Failed to sign message. {e}");
}
let wallet = match DirectSecp256k1HdWallet::checked_from_mnemonic(
prefix,
mnemonic.expect("mnemonic not set"),
) {
Ok(wallet) => wallet,
Err(err) => {
error!("Could not derive an account key from the mnemonic: {err}");
return;
}
};
match wallet.get_accounts().first() {
Some(account) => {
let msg = args.message.into_bytes();
match wallet.sign_raw_with_account(account, msg) {
Ok(signature) => {
let output = SignatureOutputJson {
account_id: account.address().to_string(),
public_key: account.public_key(),
signature_as_hex: signature.to_string(),
};
println!("{}", json!(output));
}
Err(e) => {
error!("Failed to sign message. {e}");
}
}
None => {
error!("Could not derive an account key from the mnemonic",)
}
},
Err(e) => {
error!("Failed to derive accounts. {e}");
}
None => {
error!("Could not derive an account key from the mnemonic",)
}
}
}
+19
View File
@@ -1,6 +1,7 @@
// Copyright 2025 Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_crypto::asymmetric::ed25519;
use nym_ecash_signer_check::SignerCheckError;
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::{EpochId, error::NymAPIError};
@@ -168,6 +169,24 @@ pub enum CredentialProxyError {
device_id: String,
credential_id: String,
},
#[error(
"the attestation check url has not been provided through either the CLI nor the default .env config"
)]
AttestationCheckUrlNotSet,
#[error("the provided attester public key is malformed: {source}")]
MalformedAttestationCheckUrl { source: url::ParseError },
#[error(
"the attester public key has not been provided through either the CLI nor the default .env config"
)]
AttesterPublicKeyNotSet,
#[error("the provided attester public key is malformed: {source}")]
MalformedAttesterPublicKey {
source: ed25519::Ed25519RecoveryError,
},
}
impl From<NymAPIError> for CredentialProxyError {
+1
View File
@@ -14,6 +14,7 @@ bincode = { workspace = true, optional = true }
log = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true, features = ["derive"], optional = true }
time = { workspace = true }
tokio = { workspace = true, features = ["sync"] }
zeroize = { workspace = true, features = ["zeroize_derive"] }
@@ -0,0 +1,17 @@
/*
* Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
CREATE TABLE emergency_credential
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL,
-- don't define any strict schema on the content as it might be implementation-dependant
content BLOB NOT NULL,
expiration TIMESTAMP WITHOUT TIME ZONE
);
-- no point in allowing duplicate data
CREATE UNIQUE INDEX emergency_credential_unique_type_content
ON emergency_credential (type, content);
@@ -1,7 +1,10 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::models::{BasicTicketbookInformation, RetrievedPendingTicketbook, RetrievedTicketbook};
use crate::models::{
BasicTicketbookInformation, EmergencyCredential, EmergencyCredentialContent,
RetrievedPendingTicketbook, RetrievedTicketbook,
};
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
use nym_compact_ecash::VerificationKeyAuth;
@@ -22,6 +25,12 @@ pub struct MemoryEcachTicketbookManager {
inner: Arc<RwLock<EcashCredentialManagerInner>>,
}
#[derive(Default)]
struct InternalIdCounters {
next_ticketbook_id: i64,
next_emergency_credential_id: i64,
}
#[derive(Default)]
struct EcashCredentialManagerInner {
ticketbooks: HashMap<i64, RetrievedTicketbook>,
@@ -29,13 +38,22 @@ struct EcashCredentialManagerInner {
master_vk: HashMap<u64, VerificationKeyAuth>,
coin_indices_sigs: HashMap<u64, Vec<AnnotatedCoinIndexSignature>>,
expiration_date_sigs: HashMap<(u64, Date), Vec<AnnotatedExpirationDateSignature>>,
_next_id: i64,
emergency_credentials: HashMap<String, Vec<EmergencyCredential>>,
// internal counters emulating assignment of an increasing id to new inserted database entries
internal_counters: InternalIdCounters,
}
impl EcashCredentialManagerInner {
fn next_id(&mut self) -> i64 {
let next = self._next_id;
self._next_id += 1;
fn next_ticketbook_id(&mut self) -> i64 {
let next = self.internal_counters.next_ticketbook_id;
self.internal_counters.next_ticketbook_id += 1;
next
}
fn next_emergency_credential_id(&mut self) -> i64 {
let next = self.internal_counters.next_emergency_credential_id;
self.internal_counters.next_emergency_credential_id += 1;
next
}
}
@@ -170,7 +188,7 @@ impl MemoryEcachTicketbookManager {
used_tickets: u32,
) {
let mut guard = self.inner.write().await;
let id = guard.next_id();
let id = guard.next_ticketbook_id();
#[allow(clippy::unwrap_used)]
let mut nasty_clone = hack_clone_ticketbook(ticketbook);
@@ -277,4 +295,41 @@ impl MemoryEcachTicketbookManager {
sigs.signatures.clone(),
);
}
pub(crate) async fn get_emergency_credential(&self, typ: &str) -> Option<EmergencyCredential> {
let guard = self.inner.read().await;
guard.emergency_credentials.get(typ)?.first().cloned()
}
pub(crate) async fn insert_emergency_credential(
&self,
credential: &EmergencyCredentialContent,
) {
let mut guard = self.inner.write().await;
let id = guard.next_emergency_credential_id();
guard
.emergency_credentials
.entry(credential.typ.clone())
.or_default()
.push(EmergencyCredential {
id,
data: credential.clone(),
});
}
pub(crate) async fn remove_emergency_credential(&self, id: i64) {
let mut guard = self.inner.write().await;
guard.emergency_credentials.retain(|_, credentials| {
credentials.retain(|c| c.id != id);
!credentials.is_empty()
})
}
pub(crate) async fn remove_emergency_credentials_of_type(&self, typ: &str) {
let mut guard = self.inner.write().await;
guard.emergency_credentials.remove(typ);
}
}
@@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::models::{
BasicTicketbookInformation, RawCoinIndexSignatures, RawExpirationDateSignatures,
RawVerificationKey, StoredIssuedTicketbook, StoredPendingTicketbook,
BasicTicketbookInformation, EmergencyCredential, EmergencyCredentialContent,
RawCoinIndexSignatures, RawExpirationDateSignatures, RawVerificationKey,
StoredIssuedTicketbook, StoredPendingTicketbook,
};
use nym_ecash_time::Date;
use sqlx::{Executor, Sqlite, Transaction};
@@ -305,6 +306,74 @@ impl SqliteEcashTicketbookManager {
.await?;
Ok(())
}
pub(crate) async fn get_emergency_credential(
&self,
typ: &str,
) -> Result<Option<EmergencyCredential>, sqlx::Error> {
sqlx::query_as(
r#"
SELECT *
FROM emergency_credential
WHERE type = ?
AND (expiration IS NULL OR expiration > CURRENT_TIMESTAMP)
ORDER BY expiration DESC NULLS LAST
LIMIT 1
"#,
)
.bind(typ)
.fetch_optional(&*self.connection_pool)
.await
}
pub(crate) async fn insert_emergency_credential(
&self,
credential: &EmergencyCredentialContent,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO emergency_credential
(type, content, expiration)
VALUES (?, ?, ?)
ON CONFLICT(type, content) DO NOTHING;
"#,
credential.typ,
credential.content,
credential.expiration,
)
.execute(&*self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn remove_emergency_credential(&self, id: i64) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
DELETE FROM emergency_credential
WHERE id = ?
"#,
id
)
.execute(&*self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn remove_emergency_credentials_of_type(
&self,
typ: &str,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
DELETE FROM emergency_credential
WHERE type = ?
"#,
typ
)
.execute(&*self.connection_pool)
.await?;
Ok(())
}
}
pub(crate) async fn get_next_unspent_ticketbook<'a, E>(
@@ -3,7 +3,10 @@
use crate::backends::memory::MemoryEcachTicketbookManager;
use crate::error::StorageError;
use crate::models::{BasicTicketbookInformation, RetrievedPendingTicketbook, RetrievedTicketbook};
use crate::models::{
BasicTicketbookInformation, EmergencyCredential, EmergencyCredentialContent,
RetrievedPendingTicketbook, RetrievedTicketbook,
};
use crate::storage::Storage;
use async_trait::async_trait;
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
@@ -218,6 +221,38 @@ impl Storage for EphemeralStorage {
.await;
Ok(())
}
async fn get_emergency_credential(
&self,
typ: &str,
) -> Result<Option<EmergencyCredential>, Self::StorageError> {
Ok(self.storage_manager.get_emergency_credential(typ).await)
}
async fn insert_emergency_credential(
&self,
credential: &EmergencyCredentialContent,
) -> Result<(), Self::StorageError> {
self.storage_manager
.insert_emergency_credential(credential)
.await;
Ok(())
}
async fn remove_emergency_credential(&self, id: i64) -> Result<(), Self::StorageError> {
self.storage_manager.remove_emergency_credential(id).await;
Ok(())
}
async fn remove_emergency_credentials_of_type(
&self,
typ: &str,
) -> Result<(), Self::StorageError> {
self.storage_manager
.remove_emergency_credentials_of_type(typ)
.await;
Ok(())
}
}
#[cfg(test)]
+18
View File
@@ -3,6 +3,7 @@
use nym_credentials::{IssuanceTicketBook, IssuedTicketBook};
use nym_ecash_time::Date;
use time::OffsetDateTime;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub struct RetrievedTicketbook {
@@ -78,3 +79,20 @@ pub struct RawVerificationKey {
pub serialised_key: Vec<u8>,
pub serialization_revision: u8,
}
#[derive(Clone, Debug)]
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
pub struct EmergencyCredential {
pub id: i64,
#[cfg_attr(not(target_arch = "wasm32"), sqlx(flatten))]
pub data: EmergencyCredentialContent,
}
#[derive(Clone, Debug)]
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
pub struct EmergencyCredentialContent {
#[cfg_attr(not(target_arch = "wasm32"), sqlx(rename = "type"))]
pub typ: String,
pub content: Vec<u8>,
pub expiration: Option<OffsetDateTime>,
}
@@ -3,6 +3,7 @@
mod legacy_helpers;
use crate::models::{EmergencyCredential, EmergencyCredentialContent};
use crate::{
backends::sqlite::{
get_next_unspent_ticketbook, increase_used_ticketbook_tickets, SqliteEcashTicketbookManager,
@@ -401,4 +402,36 @@ impl Storage for PersistentStorage {
.await?;
Ok(())
}
async fn get_emergency_credential(
&self,
typ: &str,
) -> Result<Option<EmergencyCredential>, Self::StorageError> {
Ok(self.storage_manager.get_emergency_credential(typ).await?)
}
async fn insert_emergency_credential(
&self,
credential: &EmergencyCredentialContent,
) -> Result<(), Self::StorageError> {
self.storage_manager
.insert_emergency_credential(credential)
.await?;
Ok(())
}
async fn remove_emergency_credential(&self, id: i64) -> Result<(), Self::StorageError> {
self.storage_manager.remove_emergency_credential(id).await?;
Ok(())
}
async fn remove_emergency_credentials_of_type(
&self,
typ: &str,
) -> Result<(), Self::StorageError> {
self.storage_manager
.remove_emergency_credentials_of_type(typ)
.await?;
Ok(())
}
}
+21 -1
View File
@@ -1,7 +1,10 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::models::{BasicTicketbookInformation, RetrievedPendingTicketbook, RetrievedTicketbook};
use crate::models::{
BasicTicketbookInformation, EmergencyCredential, EmergencyCredentialContent,
RetrievedPendingTicketbook, RetrievedTicketbook,
};
use async_trait::async_trait;
use nym_compact_ecash::VerificationKeyAuth;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
@@ -108,4 +111,21 @@ pub trait Storage: Clone + Send + Sync {
&self,
signatures: &AggregatedExpirationDateSignatures,
) -> Result<(), Self::StorageError>;
async fn get_emergency_credential(
&self,
typ: &str,
) -> Result<Option<EmergencyCredential>, Self::StorageError>;
async fn insert_emergency_credential(
&self,
credential: &EmergencyCredentialContent,
) -> Result<(), Self::StorageError>;
async fn remove_emergency_credential(&self, id: i64) -> Result<(), Self::StorageError>;
async fn remove_emergency_credentials_of_type(
&self,
typ: &str,
) -> Result<(), Self::StorageError>;
}
+2 -1
View File
@@ -17,7 +17,6 @@ cosmwasm-std = { workspace = true }
cw-utils = { workspace = true }
dyn-clone = { workspace = true }
futures = { workspace = true }
rand = { workspace = true }
si-scale = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
@@ -27,8 +26,10 @@ tracing = { workspace = true }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-gateway-requests = { path = "../gateway-requests" }
nym-gateway-storage = { path = "../gateway-storage" }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-upgrade-mode-check = { path = "../upgrade-mode-check" }
@@ -6,7 +6,6 @@ use crate::ClientBandwidth;
use crate::error::*;
use nym_credentials::ecash::utils::ecash_today;
use nym_credentials_interface::Bandwidth;
use nym_gateway_requests::ServerResponse;
use nym_gateway_storage::traits::BandwidthGatewayStorage;
use si_scale::helpers::bibytes2;
use time::OffsetDateTime;
@@ -66,7 +65,7 @@ impl BandwidthStorageManager {
Ok(())
}
pub async fn handle_claim_testnet_bandwidth(&mut self) -> Result<ServerResponse> {
pub async fn handle_claim_testnet_bandwidth(&mut self) -> Result<i64> {
debug!("handling testnet bandwidth request");
if self.only_coconut_credentials {
@@ -76,8 +75,7 @@ impl BandwidthStorageManager {
self.increase_bandwidth(FREE_TESTNET_BANDWIDTH_VALUE, ecash_today())
.await?;
let available_total = self.client_bandwidth.available().await;
Ok(ServerResponse::Bandwidth { available_total })
Ok(available_total)
}
#[instrument(skip_all)]
@@ -96,7 +94,7 @@ impl BandwidthStorageManager {
let available_bi2 = bibytes2(available_bandwidth as f64);
let required_bi2 = bibytes2(required_bandwidth as f64);
debug!(available = available_bi2, required = required_bi2);
trace!(available = available_bi2, required = required_bi2);
self.consume_bandwidth(required_bandwidth).await?;
let remaining_bandwidth = self.client_bandwidth.available().await;
@@ -47,6 +47,25 @@ pub enum Error {
UnknownTicketType(#[from] nym_credentials_interface::UnknownTicketType),
}
impl Error {
pub fn is_out_of_bandwidth(&self) -> bool {
matches!(self, Error::OutOfBandwidth { .. })
}
}
pub trait OutOfBandwidthResultExt {
fn is_out_of_bandwidth(&self) -> bool;
}
impl<T> OutOfBandwidthResultExt for Result<T> {
fn is_out_of_bandwidth(&self) -> bool {
match &self {
Ok(_) => false,
Err(err) => err.is_out_of_bandwidth(),
}
}
}
impl From<EcashTicketError> for Error {
fn from(err: EcashTicketError) -> Self {
// don't expose storage issue details to the user
@@ -13,11 +13,13 @@ use tracing::*;
pub use client_bandwidth::*;
pub use error::*;
pub use upgrade_mode::UpgradeModeState;
pub mod bandwidth_storage_manager;
mod client_bandwidth;
pub mod ecash;
pub mod error;
pub mod upgrade_mode;
pub struct CredentialVerifier {
credential: CredentialSpendingRequest,

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