Compare commits

...

341 Commits

Author SHA1 Message Date
Jon Häggblad bc59f94c1b Pass errors up the call stack 2024-05-06 11:56:30 +02:00
Jon Häggblad 8c28a12569 Start adding error return types 2024-05-06 10:24:50 +02:00
Jon Häggblad f7387c3229 wip: explore timeout on ws send 2024-05-05 22:42:56 +02:00
Jon Häggblad a3460d7f67 wip 2024-05-05 21:05:01 +02:00
Jon Häggblad d7b5f4f6d6 Exlicitly drop packet router when shutdown detected
In the vpn client I've observed that 2 out of 3 times the disconnect
procedure stalls out.

Investigating in the logs these 5 are still running when we hit shutdown
timeout.

[TaskClient-nym_vpn_lib-mixnet_client_main-real_traffic_controller-ack_control-retransmission_request_listener] Polling shutdown failed: channel closed
[TaskClient-nym_vpn_lib-mixnet_client_main-real_traffic_controller-out_queue_control] Polling shutdown failed: channel closed
[TaskClient-gateway-packet-router] Polling shutdown failed: channel closed
[TaskClient-nym_vpn_lib-mixnet_client_main-gateway_transceiver] Polling shutdown failed: channel closed
[TaskClient-nym_vpn_lib-mixnet_client_main-mix_traffic_controller] Polling shutdown failed: channel closed

I _think_ what was causing the problem here is that the task client in
the packet router is not being awaited in an event loop like other
client instances.

Explictly drop it in the socket state to make sure it's not blocking
shutdown.
2024-05-05 21:05:01 +02:00
Tommy Verrall c610389198 Merge pull request #4549 from nymtech/dependabot/go_modules/wasm/mix-fetch/go-mix-conn/golang.org/x/net-0.23.0
Bump golang.org/x/net from 0.17.0 to 0.23.0 in /wasm/mix-fetch/go-mix-conn
2024-04-19 17:46:36 +02:00
dependabot[bot] d283ecae22 Bump golang.org/x/net in /wasm/mix-fetch/go-mix-conn
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.17.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.17.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-19 12:20:51 +00:00
Jędrzej Stuczyński 2120acfdad Merge pull request #4547 from nymtech/feature/update-nymnode-bonding-version
Feature/update nymnode bonding version
2024-04-19 10:12:18 +01:00
Jędrzej Stuczyński b613775551 updated post-migration message 2024-04-18 15:16:18 +01:00
Jędrzej Stuczyński 73d4896b0b use {nym-node-binary-version}+nymnode rather than {nym-mixnode-binary-version}+nymnode for bonding info 2024-04-18 15:03:28 +01:00
Simon Wicky b8b66fa4ad rerandomize outfox's secret for each hop (#4544) 2024-04-18 13:48:08 +02:00
Jędrzej Stuczyński ba59022160 Merge pull request #4546 from nymtech/feature/disable-noise-key-announcement
don't announce noise keys
2024-04-18 12:18:24 +01:00
Jędrzej Stuczyński 8e2c64a867 don't announce noise keys 2024-04-18 11:25:29 +01:00
benedetta davico 73ae09cb76 Update Cargo.toml (#4543) 2024-04-17 14:00:58 +02:00
Simon Wicky fd238b1d1b fix dependency workspace typo (#4542) 2024-04-17 10:14:00 +02:00
Jon Häggblad 1f2d888626 Handle selected gateway in rust sdk (#4515)
* Set active gateway after setting up gateway in base client

* Elevate log statements in gateway setup

* Add debug implementations for some gateway setup types

* Rework gateway setup in rust sdk mixnet client

* Remove unused KeyMode type

* Remove pub from internal setters

* Remove pseudo builder methods

* Make create_bandwidth_client pub

* Downgrade log statement to debug

* Revert set_active_gateway in base_client

* Rename to set_active_gateway_if_previously_registered
2024-04-15 14:11:26 +02:00
Tommy Verrall 9f47b05ed6 Merge pull request #4537 from nymtech/dependabot/npm_and_yarn/wasm/mix-fetch/internal-dev-node/tar-6.2.1
Bump tar from 6.1.15 to 6.2.1 in /wasm/mix-fetch/internal-dev-node
2024-04-15 09:21:37 +01:00
Tommy Verrall f559a03732 Merge pull request #4538 from nymtech/dependabot/npm_and_yarn/wasm/client/internal-dev-node/tar-6.2.1
Bump tar from 6.1.15 to 6.2.1 in /wasm/client/internal-dev-node
2024-04-15 09:21:06 +01:00
Jędrzej Stuczyński 3e12766afd expose info whether entry gateway enforces zk-nym in self-described api (#4529) 2024-04-12 19:05:28 +02:00
Jędrzej Stuczyński 1c93cc8a68 Merge pull request #4437 from nymtech/feature/freepass-expiration
expire freepass bandwidth + stagger db flushes
2024-04-12 17:48:37 +01:00
Jędrzej Stuczyński 4865f7f205 [bugfix] Prevent generation of free passes with expiry date in the past (#4535) 2024-04-12 17:44:32 +02:00
Jędrzej Stuczyński a785950d76 [bugfix] don't shutdown NR on failed connection (#4539) 2024-04-12 17:43:22 +02:00
Jędrzej Stuczyński 328d1c25b7 removed old debug code 2024-04-12 15:23:37 +01:00
Jędrzej Stuczyński 862a248902 missing cli argument for controlling nyxd 2024-04-12 15:14:55 +01:00
Jędrzej Stuczyński f826904407 fix credential expiration + improve errors + naively stop client when out of bandwidth 2024-04-12 15:00:49 +01:00
Jędrzej Stuczyński 67467ca76d renamed 'shared_state' to 'common_state' 2024-04-12 15:00:49 +01:00
Jędrzej Stuczyński a53ae5b6b5 using lock guard in dedicated scope 2024-04-12 15:00:49 +01:00
Jędrzej Stuczyński bfc495ef29 expire freepass bandwidth + stagger db flushes 2024-04-12 15:00:48 +01:00
Jędrzej Stuczyński 9d74c22f9b Merge pull request #4536 from nymtech/bugfix/localnet
[bugfix] make sure localnet script works in post nym-node era
2024-04-12 12:41:53 +01:00
import this f978552a3a [DOC]: NymVPN testing Guides update (#4528)
* simplify cli - comment redundant pages

* unite mac and linux cli guide to one page

* finalise cli guide

* syntax edit

* book built

* syntax edit

* final version - ready to review

* final version - ready to review
2024-04-11 12:09:58 +00:00
dependabot[bot] b2477dd81b Bump tar from 6.1.15 to 6.2.1 in /wasm/client/internal-dev-node
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 10:51:16 +00:00
dependabot[bot] dcd712430e Bump tar from 6.1.15 to 6.2.1 in /wasm/mix-fetch/internal-dev-node
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 10:51:15 +00:00
Jędrzej Stuczyński 5dcad5ffd4 [bugfix] make sure localnet script works in post nym-node era 2024-04-11 10:25:04 +01:00
Jędrzej Stuczyński 210269cb9c [bugfix] restore old serialisation of 'NymNodeDescription' (#4533) 2024-04-11 10:11:46 +02:00
Jędrzej Stuczyński 8c59106add introduced /api-status routes for nym-api (#4527)
* introduced /api-status routes for nym-api

* added additional information to the signer-information endpoint
2024-04-11 09:28:42 +02:00
Tommy Verrall 264771f8cf Merge pull request #4428 from nymtech/sachin/delegate_from_file
nym-cli: delegations from a file
2024-04-10 15:36:11 +01:00
Tommy Verrall 28d27eb84e Merge pull request #4517 from nymtech/dependabot/cargo/sdk/ffi/shared/eyre-0.6.12
Bump eyre from 0.6.11 to 0.6.12 in /sdk/ffi/shared
2024-04-10 11:16:27 +01:00
Tommy Verrall d57757584c Merge pull request #4518 from nymtech/dependabot/cargo/sdk/ffi/cpp/eyre-0.6.12
Bump eyre from 0.6.11 to 0.6.12 in /sdk/ffi/cpp
2024-04-10 10:35:25 +01:00
Tommy Verrall 6da00513a7 Merge pull request #4519 from nymtech/dependabot/cargo/sdk/ffi/go/eyre-0.6.12
Bump eyre from 0.6.11 to 0.6.12 in /sdk/ffi/go
2024-04-10 10:25:38 +01:00
dependabot[bot] 035faf70e4 Bump eyre from 0.6.11 to 0.6.12 in /sdk/ffi/cpp
Bumps [eyre](https://github.com/eyre-rs/eyre) from 0.6.11 to 0.6.12.
- [Commits](https://github.com/eyre-rs/eyre/commits)

---
updated-dependencies:
- dependency-name: eyre
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-10 09:02:46 +00:00
Tommy Verrall b238d108e6 Merge pull request #4520 from nymtech/dependabot/cargo/sdk/ffi/cpp/h2-0.3.26
Bump h2 from 0.3.24 to 0.3.26 in /sdk/ffi/cpp
2024-04-10 10:01:47 +01:00
dependabot[bot] 5581b15094 Bump eyre from 0.6.11 to 0.6.12 in /sdk/ffi/go
Bumps [eyre](https://github.com/eyre-rs/eyre) from 0.6.11 to 0.6.12.
- [Commits](https://github.com/eyre-rs/eyre/commits)

---
updated-dependencies:
- dependency-name: eyre
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-10 08:34:13 +00:00
Tommy Verrall af19c4ecfd Merge pull request #4521 from nymtech/dependabot/cargo/sdk/ffi/go/h2-0.3.26
Bump h2 from 0.3.24 to 0.3.26 in /sdk/ffi/go
2024-04-10 09:31:31 +01:00
Jędrzej Stuczyński 1855423981 Merge pull request #4531 from nymtech/bugfix/self-described-backwards-compat
Bugfix/self described backwards compat
2024-04-09 16:49:18 +01:00
benedetta davico 229561bab9 update sandbox .env (#4530) 2024-04-09 17:47:30 +02:00
Jędrzej Stuczyński 7d2044c177 Merge pull request #4532 from nymtech/bugfix/standalone-node-http-api
run http api by default
2024-04-09 16:47:20 +01:00
Jędrzej Stuczyński 7885d3c986 removed dead code
removed dead code
2024-04-09 16:36:05 +01:00
Jędrzej Stuczyński 78fb3c2293 run http api by default 2024-04-09 16:26:54 +01:00
Jędrzej Stuczyński 0ed43d2439 allow nym-api to understand signatures on legacy host information 2024-04-09 16:17:39 +01:00
Jędrzej Stuczyński 69c1a32392 error prefix 2024-04-09 16:16:39 +01:00
Sachin Kamath 30ffea19a1 docs: add credential signing steps for nym-api (#4523)
* docs: add credential signing steps for nym-api

* review comments
2024-04-09 10:04:24 +02:00
Jon Häggblad 9e3bd8588d Re-export TransmissionLane type (#4525) 2024-04-08 16:05:12 +02:00
import this ac7b821782 [DOC]: Feature: cmdrun scripts (#4514)
* initialise cmdrun script dir

* initialise api_targets_config

* initialise api_targets

* first version of argparser cli

* create a token api stats table

* add format function

* initiate nym_vpn command

* add github to get api url

* add github api to config

* create nym_vpn releases dataframe

* menu headers tweak
2024-04-08 09:13:31 +00:00
dependabot[bot] bb372fb35c Bump eyre from 0.6.11 to 0.6.12 in /sdk/ffi/shared
Bumps [eyre](https://github.com/eyre-rs/eyre) from 0.6.11 to 0.6.12.
- [Commits](https://github.com/eyre-rs/eyre/commits)

---
updated-dependencies:
- dependency-name: eyre
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-08 08:28:10 +00:00
Tommy Verrall 5f4220867a Merge pull request #4522 from nymtech/dependabot/cargo/sdk/ffi/shared/h2-0.3.26
Bump h2 from 0.3.24 to 0.3.26 in /sdk/ffi/shared
2024-04-08 09:27:12 +01:00
Jon Häggblad bfa029f284 Use client-core default db names in sdk (#4524) 2024-04-06 09:54:53 +02:00
Jędrzej Stuczyński 4c5351ba60 Feature/one binary to rule them all (#4500) 2024-04-05 19:34:31 +02:00
dependabot[bot] 3a6058be60 Bump h2 from 0.3.24 to 0.3.26 in /sdk/ffi/shared
Bumps [h2](https://github.com/hyperium/h2) from 0.3.24 to 0.3.26.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.26/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.24...v0.3.26)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 16:24:23 +00:00
dependabot[bot] 7fcc8cdd19 Bump h2 from 0.3.24 to 0.3.26 in /sdk/ffi/go
Bumps [h2](https://github.com/hyperium/h2) from 0.3.24 to 0.3.26.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.26/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.24...v0.3.26)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 16:24:03 +00:00
dependabot[bot] 098bd4eb3c Bump h2 from 0.3.24 to 0.3.26 in /sdk/ffi/cpp
Bumps [h2](https://github.com/hyperium/h2) from 0.3.24 to 0.3.26.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.26/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.24...v0.3.26)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 16:18:54 +00:00
Jon Häggblad fa1e9988b3 Add credentials mode command to SDK mixnet builder (#4516) 2024-04-05 15:51:48 +02:00
Tommy Verrall 999b4f743e Merge pull request #4470 from nymtech/feature/explorer-delegation-mobile
Feature/explorer delegation mobile
2024-04-02 08:20:26 +01:00
Tommy Verrall cc55abf7fb Merge pull request #4510 from nymtech/dependabot/npm_and_yarn/clients/native/examples/js-examples/websocket/express-4.19.2
Bump express from 4.18.2 to 4.19.2 in /clients/native/examples/js-examples/websocket
2024-04-02 08:17:35 +01:00
Tommy Verrall 020edb8f84 Merge pull request #4512 from nymtech/dependabot/npm_and_yarn/wasm/node-tester/internal-dev/express-4.19.2
Bump express from 4.18.2 to 4.19.2 in /wasm/node-tester/internal-dev
2024-04-02 08:17:05 +01:00
Tommy Verrall ae2079272b Merge pull request #4511 from nymtech/dependabot/npm_and_yarn/wasm/client/internal-dev/express-4.19.2
Bump express from 4.18.2 to 4.19.2 in /wasm/client/internal-dev
2024-04-02 08:16:53 +01:00
Tommy Verrall a656429376 Merge pull request #4513 from nymtech/dependabot/npm_and_yarn/wasm/mix-fetch/internal-dev/express-4.19.2
Bump express from 4.18.2 to 4.19.2 in /wasm/mix-fetch/internal-dev
2024-04-02 08:13:49 +01:00
dependabot[bot] 2086946e16 Bump express from 4.18.2 to 4.19.2 in /wasm/mix-fetch/internal-dev
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-29 08:31:27 +00:00
dependabot[bot] 08e0892327 Bump express from 4.18.2 to 4.19.2 in /wasm/node-tester/internal-dev
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-29 08:31:25 +00:00
dependabot[bot] d28783cb2a Bump express from 4.18.2 to 4.19.2 in /wasm/client/internal-dev
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-29 08:31:24 +00:00
dependabot[bot] 1815a2d3a5 Bump express in /clients/native/examples/js-examples/websocket
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-28 18:44:49 +00:00
Tommy Verrall 1429f63d9d Merge pull request #4464 from nymtech/feature/remove-allowlist
removed the usage of allow.list
2024-03-28 16:10:59 +00:00
Jędrzej Stuczyński 1ed087e801 fixed NR template 2024-03-28 15:43:37 +00:00
Jędrzej Stuczyński 7fce0a9692 remove explicit deref 2024-03-28 15:43:37 +00:00
Jędrzej Stuczyński bef0a537b2 config migration for removing allow.list 2024-03-28 15:43:37 +00:00
Jędrzej Stuczyński fb2b2c963b moved and renamed NR old config structs 2024-03-28 15:43:37 +00:00
Jędrzej Stuczyński 2aa5e25532 removed the usage of allow.list 2024-03-28 15:43:36 +00:00
Tommy Verrall 588ed7dead Merge pull request #4509 from nymtech/bugfix/metrics-precondition
bugfix: use OptionFuture for creating metrics server
2024-03-28 15:39:33 +00:00
Jędrzej Stuczyński 519d419bbf bugfix: use OptionFuture for creating metrics server 2024-03-28 15:16:28 +00:00
fmtabbara f5e72cefcc Update dependencies in yarn.lock file 2024-03-28 10:15:16 +00:00
Jędrzej Stuczyński a5a9b5128f Merge pull request #4508 from nymtech/chore/beta-CI
fixed beta clippy: lints + updated cosmwasm to 1.4
2024-03-28 10:12:33 +00:00
Jędrzej Stuczyński 94cfac0bae Merge pull request #4463 from nymtech/feature/multigateway-config
Feature/multigateway config
2024-03-28 10:04:06 +00:00
import this 6fc4a06b3b [DOC]: Serinko/operators/fast-and-furious (#4466)
* initialise performance testing pages

* draft perfomance testing flow

* initialise metric template page

* add guides TOC

* spell check

* edit metrics monitoring page

* syntax edit

* draft initial  prometheus setup flow

* add prometheus config

* setup prometheus systemd service

* add self-hosted-monitor scripts

* steps to run explorenym self-hosted monitor

* add screenshots and explain steps

* changed test page url and syntax edit

* add grafana info

* finalise explorenym scripts page and initialise docker page

* initialise node setup page

* clarify remaining todos and sort summary

* add initial node setup steps

* update node-setup

* update docker setup layout

* syntax edit

* simplify node-setup and add troubleshooting

* fix typo

* implement feedback

* new release version

* remove sandbox from nymvpn gui guides

* cleaning after FF testing

* add blacklist troubleshooting

* added run command

* address review comments
2024-03-28 10:33:06 +01:00
Jędrzej Stuczyński 91b790accc fixed incorrect ArgGroup naming 2024-03-28 09:30:27 +00:00
Jędrzej Stuczyński c585753d02 removing unused code 2024-03-28 09:10:57 +00:00
Jędrzej Stuczyński 0b46d64869 cargo fmt 2024-03-28 08:57:17 +00:00
Jędrzej Stuczyński 4c43935862 change NR+IPR return types 2024-03-28 08:49:30 +00:00
Jędrzej Stuczyński 1804c66a96 CI: remove ephemera contract upload 2024-03-28 08:12:41 +00:00
Jędrzej Stuczyński 8fa213502d removed force register flag + improved error message on no gateways available 2024-03-28 08:11:57 +00:00
Jędrzej Stuczyński b869c30909 added extra alias 2024-03-27 17:22:30 +00:00
Jędrzej Stuczyński f012a1a069 fixed add gateway commands for nr and ipr 2024-03-27 17:22:30 +00:00
Jędrzej Stuczyński 74d03bfb3b moved for adding new gateways to separate command and made client init only first time setup related 2024-03-27 17:22:30 +00:00
Jędrzej Stuczyński ff499869d3 clippy 2024-03-27 17:22:30 +00:00
Jędrzej Stuczyński b8ab07020c fixed eda61b57dc8d349a9b0af70cbd02743eba1e10bf 2024-03-27 17:22:29 +00:00
Jędrzej Stuczyński dcd3d80b24 fixed post-rebasing clippy issues 2024-03-27 17:22:28 +00:00
Jędrzej Stuczyński ebd1eeb38d allow gateways to migrate configs of embedded NR/IPR 2024-03-27 17:20:39 +00:00
Jędrzej Stuczyński 36f84ca18f fixed typo in SQL query 2024-03-27 17:20:39 +00:00
Jędrzej Stuczyński 6259106b6b fixed embedded SP not correctly setting active gateway 2024-03-27 17:20:39 +00:00
Jędrzej Stuczyński 73d6e704c4 fixed gateway not generating keys for nr and ipr 2024-03-27 17:20:39 +00:00
Jędrzej Stuczyński c95e424981 missing licenses for new crates 2024-03-27 17:20:39 +00:00
Jędrzej Stuczyński 06abe53399 CT-80 2024-03-27 17:20:39 +00:00
Jędrzej Stuczyński 7c14a92baa decreased logging level 2024-03-27 17:20:38 +00:00
Jędrzej Stuczyński c07b782afa added bool to list gateways to indicate currently active gateway 2024-03-27 17:20:38 +00:00
Jędrzej Stuczyński 062f4517d6 added cli command for switching active gateway 2024-03-27 17:20:38 +00:00
Jędrzej Stuczyński 28b02c83db unified credential import cli + added it for ipr 2024-03-27 17:20:36 +00:00
Jędrzej Stuczyński 1b5a0f8cf2 list gateways cli command 2024-03-27 17:20:04 +00:00
Jędrzej Stuczyński 6c0573cb01 split up InitialisableClient trait 2024-03-27 17:20:04 +00:00
Jędrzej Stuczyński b59487cfb0 fixed ipr imports 2024-03-27 17:20:04 +00:00
Jędrzej Stuczyński 0ac8098dad ability to reuse free passes from the storage for new gateways 2024-03-27 17:20:03 +00:00
Jędrzej Stuczyński 9b10871efb ipr config migration 2024-03-27 17:20:03 +00:00
Jędrzej Stuczyński e48af11e8f clippy et. al 2024-03-27 17:20:03 +00:00
Jędrzej Stuczyński a4e5b2af93 further wasm fixes 2024-03-27 17:20:03 +00:00
Jędrzej Stuczyński 67c5a36894 building wasm-client-core 2024-03-27 17:20:03 +00:00
Jędrzej Stuczyński 765ac715c1 fixing builds of x86-based binaries 2024-03-27 17:20:03 +00:00
Jędrzej Stuczyński c4bc156cac actual sql 2024-03-27 17:20:03 +00:00
Jędrzej Stuczyński 40f08dcbb2 impl GatewaysDetailsStore for OnDiskGatewaysDetails
without the underlying sql
2024-03-27 17:20:02 +00:00
Jędrzej Stuczyński b4e45ef3ef fixed active gateway detection in rust sdk 2024-03-27 17:20:02 +00:00
Jędrzej Stuczyński fc43cb590b further adjusting storage and setup to allow for wg use 2024-03-27 17:20:02 +00:00
Jędrzej Stuczyński 6e1b869c99 ibid for network requester 2024-03-27 17:20:02 +00:00
Jędrzej Stuczyński 35d84b6e42 ibid for socks5 2024-03-27 17:20:02 +00:00
Jędrzej Stuczyński d31fddf940 gateway info migration + impl for native client
other clients are broken
2024-03-27 17:20:02 +00:00
Jędrzej Stuczyński 14ee279ec8 dead code 2024-03-27 17:20:02 +00:00
Jędrzej Stuczyński a3b1a7337d checking for already registered gateways 2024-03-27 17:20:01 +00:00
Jędrzej Stuczyński c7f7e77099 updated config templates 2024-03-27 17:20:01 +00:00
Jędrzej Stuczyński 5bb96bf42c additional fixes to key creation 2024-03-27 17:20:01 +00:00
Jędrzej Stuczyński c0ae924c58 move storage helpers 2024-03-27 17:20:01 +00:00
Jędrzej Stuczyński daa5f01683 further compilation fixes 2024-03-27 17:20:01 +00:00
Jędrzej Stuczyński 337d53b2ec ibid for network requester 2024-03-27 17:20:01 +00:00
Jędrzej Stuczyński cdc49e0749 ibid for rust sdk 2024-03-27 17:20:01 +00:00
Jędrzej Stuczyński 1916adedcc socks5 client compiling
but definitely not working yet
2024-03-27 17:20:00 +00:00
Jędrzej Stuczyński a3132b907a further propagation of new gateway/key types 2024-03-27 17:20:00 +00:00
Jędrzej Stuczyński 0e09826140 initial work on client config migration 2024-03-27 17:20:00 +00:00
Jędrzej Stuczyński bf4f16c7b9 adding additional methods to the trait 2024-03-27 17:20:00 +00:00
Jędrzej Stuczyński 6915fef99f removing generics from 'GatewaySelectionSpecification' 2024-03-27 17:20:00 +00:00
Jędrzej Stuczyński c80c0b88bf starting to weave in new storage trait 2024-03-27 17:20:00 +00:00
Jędrzej Stuczyński fe2edd56a8 adjusting the trait 2024-03-27 17:20:00 +00:00
Jędrzej Stuczyński 84a36c00b4 added registration timestamp 2024-03-27 17:19:59 +00:00
Jędrzej Stuczyński 4c9de843c5 types 2024-03-27 17:19:59 +00:00
Jędrzej Stuczyński f7edc223e9 initial db schema 2024-03-27 17:19:59 +00:00
Jędrzej Stuczyński f0072ce828 moved reply surb storage to separate subcrate 2024-03-27 17:19:58 +00:00
fmtabbara 1223f50fe8 Merge branch 'develop' into feature/explorer-delegation-mobile 2024-03-27 16:29:58 +00:00
Jędrzej Stuczyński 297cde513b turns out 'make' doesn't run cargo fmt 2024-03-27 16:11:21 +00:00
Jędrzej Stuczyński 7e6feb0cd2 fixed beta clippy: lints + updated cosmwasm to 1.4 2024-03-27 16:04:51 +00:00
Tommy Verrall 0a213990a7 Merge pull request #4490 from nymtech/feature-gate-client-metrics
Feature gate core-client metrics server
2024-03-27 15:42:07 +00:00
Tommy Verrall a44cd1cca9 Merge pull request #4506 from nymtech/int-prometheus
Switch to IntCounter and Gauge
2024-03-27 15:41:51 +00:00
durch 5fe6a2c63d Undo duplication 2024-03-27 16:31:26 +01:00
Drazen 8d46bba8eb Switch to IntCounter and Gauge 2024-03-27 16:28:34 +01:00
durch 90ff36afe8 Fix wasm 2024-03-27 16:24:41 +01:00
durch d2f482c743 Feature gate metrics-server 2024-03-27 16:23:30 +01:00
Tommy Verrall ebf9a98a1e Merge pull request #4444 from nymtech/jon/update-some-nym-prefixes
Update some nym crate prefixes
2024-03-27 15:09:40 +00:00
Tommy Verrall 578ae7f267 Merge pull request #4496 from nymtech/dependabot/npm_and_yarn/wasm/mix-fetch/internal-dev/webpack-dev-middleware-5.3.4
Bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /wasm/mix-fetch/internal-dev
2024-03-27 15:08:47 +00:00
Tommy Verrall deb5054357 Merge pull request #4495 from nymtech/dependabot/npm_and_yarn/wasm/node-tester/internal-dev/webpack-dev-middleware-5.3.4
Bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /wasm/node-tester/internal-dev
2024-03-27 15:08:10 +00:00
dependabot[bot] 089143cb3e Bump webpack-dev-middleware in /wasm/node-tester/internal-dev
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-27 15:51:35 +01:00
dependabot[bot] fa1a05886f Bump webpack-dev-middleware in /wasm/mix-fetch/internal-dev
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-27 15:48:12 +01:00
Tommy Verrall bec2cc951a Merge pull request #4507 from nymtech/bugfix/wasm-build
cherry-pick 80066d330527b7993cc85833b1b3ae96cc487bb9 to fix wasm build
2024-03-27 14:41:22 +00:00
Jędrzej Stuczyński 400f248e83 cherry-pick 80066d330527b7993cc85833b1b3ae96cc487bb9 to fix wasm build 2024-03-27 14:13:20 +00:00
Tommy Verrall 7554d0ac02 Merge pull request #4478 from nymtech/dependabot/npm_and_yarn/wasm/client/internal-dev/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6 in /wasm/client/internal-dev
2024-03-27 12:05:20 +00:00
Tommy Verrall 74556ffb0f Merge pull request #4480 from nymtech/dependabot/npm_and_yarn/wasm/node-tester/internal-dev/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6 in /wasm/node-tester/internal-dev
2024-03-27 12:04:46 +00:00
Tommy Verrall 91893ac44c Merge pull request #4499 from nymtech/feature/bonding-validation-update/TX-13
Bonding validation update
2024-03-27 12:01:09 +00:00
Tommy Verrall 033246268a Merge pull request #4503 from nymtech/dependabot/npm_and_yarn/express-4.19.2
Bump express from 4.18.2 to 4.19.2
2024-03-27 11:56:27 +00:00
Tommy Verrall 3866a9a40d Merge pull request #4479 from nymtech/dependabot/npm_and_yarn/clients/native/examples/js-examples/websocket/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6 in /clients/native/examples/js-examples/websocket
2024-03-27 10:40:03 +00:00
Tommy Verrall f56b62baa2 Merge pull request #4475 from nymtech/dependabot/npm_and_yarn/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6
2024-03-27 10:39:30 +00:00
Tommy Verrall 8782de7679 Merge pull request #4504 from nymtech/wallet-recovery-docs
Adding wallet recovery README docs
2024-03-27 10:31:17 +00:00
Tommy Verrall 05f0fad7d1 Merge pull request #4477 from nymtech/dependabot/npm_and_yarn/wasm/mix-fetch/internal-dev/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6 in /wasm/mix-fetch/internal-dev
2024-03-27 10:27:39 +00:00
Tommy Verrall a04c5c7e92 typos 2024-03-27 11:25:57 +01:00
Tommy Verrall 5b4daa23b6 adding recovery docs 2024-03-27 11:20:37 +01:00
dependabot[bot] 658b635509 Bump express from 4.18.2 to 4.19.2
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-27 09:46:12 +00:00
dependabot[bot] b73cc165ae Bump follow-redirects in /clients/native/examples/js-examples/websocket
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-27 09:45:55 +00:00
Tommy Verrall c40e69415f Merge pull request #4472 from nymtech/dependabot/npm_and_yarn/testnet-faucet/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6 in /testnet-faucet
2024-03-27 09:45:28 +00:00
Tommy Verrall 54906756db Merge pull request #4494 from nymtech/dependabot/npm_and_yarn/clients/native/examples/js-examples/websocket/webpack-dev-middleware-5.3.4
Bump webpack-dev-middleware from 5.3.1 to 5.3.4 in /clients/native/examples/js-examples/websocket
2024-03-27 09:44:57 +00:00
Jon Häggblad 7ea139b624 Move request response to version dir (#4501) 2024-03-26 12:03:05 +01:00
Jon Häggblad 3082cd5d32 Cargo.lock 2024-03-26 11:41:59 +01:00
Jon Häggblad ce08de28b5 Merge remote-tracking branch 'origin/develop' into jon/update-some-nym-prefixes 2024-03-26 11:39:29 +01:00
Simon Wicky e352c25b32 mark packet_router's shutdown as success before drop (#4491) 2024-03-25 11:31:52 +01:00
Jon Häggblad f5378e8a86 Merge pull request #4498 from nymtech/jon/ipr-codec-improvements
IPR codec improvements for icmp beacon
2024-03-25 11:12:28 +01:00
fmtabbara 9bc3a1d431 Bonding validation update
Update operator cost and profit margin validation to previous values (min 40 and min 4)
2024-03-25 10:07:26 +00:00
Tommy Verrall f68ce457f7 Merge pull request #4476 from nymtech/dependabot/npm_and_yarn/docker/typescript_client/upload_contract/follow-redirects-1.15.6
Bump follow-redirects from 1.14.9 to 1.15.6 in /docker/typescript_client/upload_contract
2024-03-25 09:45:59 +00:00
Tommy Verrall 22b3ff6bec Merge pull request #4473 from nymtech/dependabot/npm_and_yarn/nym-api/tests/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6 in /nym-api/tests
2024-03-25 09:45:03 +00:00
Jon Häggblad 2fae46d19e MixnetClientSender derive Clone 2024-03-25 10:23:06 +01:00
Tommy Verrall bd7779ec63 Merge pull request #4468 from nymtech/bugfix/optional-env-values
Bugfix/optional env values
2024-03-25 08:52:55 +00:00
Jon Häggblad f3be91741a Add ability to create a single bundles IP packet directly 2024-03-25 07:26:21 +01:00
dependabot[bot] bda9f03b21 Bump webpack-dev-middleware
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.1 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.1...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-23 03:50:24 +00:00
fmtabbara 635ae1118a Add mobile delegations beta banner 2024-03-22 12:19:54 +00:00
Tommy Verrall 6c0ea49185 Merge pull request #4469 from nymtech/chore/decrease-log-severity
decreased logging level of gateway errors associated with the websocket
2024-03-22 10:25:58 +00:00
Tommy Verrall 8fe3070b85 Merge pull request #4492 from nymtech/jon/rustls-roots
Fix TLS connection error
2024-03-22 09:13:05 +00:00
Tommy Verrall d11cf0823d Merge pull request #4493 from nymtech/jon/rustc-fixes
Fix warnings for rustc 1.77
2024-03-22 08:53:30 +00:00
Jon Häggblad fb5d775857 Fix warnings for rustc 1.77 2024-03-21 22:20:43 +01:00
Jon Häggblad d020fb0a0b Inconsequential typo in Cargo.toml 2024-03-21 21:51:03 +01:00
Jon Häggblad f66132fcef Use rustls-tls-native-roots by default in gateway-client and client-core 2024-03-21 21:43:55 +01:00
Jon Häggblad 821865cb62 Explictly add rustls to gateway-client (#4471)
* Explictly add rustls to gateway-client

* fix wasm

* formatting
2024-03-21 16:56:10 +01:00
fmtabbara 68e0dd1f29 update success tx wording 2024-03-21 14:57:12 +00:00
fmtabbara cf201cba53 only use keplr wallet + add info banner 2024-03-21 14:27:16 +00:00
Bogdan-Ștefan Neacşu c1e67cdc15 Increase subnet range for IPPR (#4487)
* Increase subnet range for IPPR

* Fix for IPv6

* Fix range

* Add unit test

* Bump version
2024-03-21 13:18:43 +02:00
Mark Sinclair 6ebe71c8a2 GitHub Actions: nym-hash-release: terminate get build info shell after 3 secs 2024-03-20 18:52:48 +00:00
Mark Sinclair e51283f9d3 GitHub Actions: nym-hash-release: output more feedback 2024-03-20 18:30:09 +00:00
Mark Sinclair bad74928a1 GitHub Actions: nym-hash-release: resolve temp directory correctly 2024-03-20 18:15:36 +00:00
Mark Sinclair 467dc6cf4a GitHub Actions: nym-hash-release: better handling of temp directory 2024-03-20 18:10:36 +00:00
Mark Sinclair ef22cb9fcd GitHub Actions: nym-hash-release 2024-03-20 17:55:17 +00:00
fmtabbara efe952fdf2 explorer package updates 2024-03-20 16:30:59 +00:00
fmtabbara 6d2e761319 Merge branch 'develop' into feature/explorer-delegation-mobile 2024-03-20 14:58:36 +00:00
Mark Sinclair 3c8c51e1c9 Update release-calculate-hash.yml 2024-03-20 14:29:50 +00:00
Mark Sinclair 480799bad1 Change to using github action reference 2024-03-20 14:28:25 +00:00
fmtabbara e699f1ad79 explorer env configuration updates 2024-03-20 13:45:29 +00:00
Tommy Verrall 0b4a1833ec Merge pull request #4484 from nymtech/more-metrics
Expose metrics from PacketStatisticsControl
2024-03-20 12:43:17 +00:00
Sachin Kamath 78b00302c8 docs: add websocket reverse proxy example (#4452)
* docs: add websocket reverse proxy example

* docs: add a line about reverse proxy

* remove note about nym-api not released
2024-03-20 12:37:02 +00:00
durch eb914463dc Fix wasm build 2024-03-20 13:27:58 +01:00
durch 9a5d6103d6 Fix gateway target generation 2024-03-20 13:25:26 +01:00
durch 7ccba11d82 Static targets script 2024-03-20 12:59:10 +01:00
durch e67d3d816c Push package name to metrics 2024-03-20 12:59:10 +01:00
durch e2aa7aa31c Relax hyper dependency 2024-03-20 12:59:10 +01:00
durch 7ecac4a7b4 Fix predictable port range :) 2024-03-20 12:59:10 +01:00
durch 0b82109e3c Predictable IP range 2024-03-20 12:59:10 +01:00
durch 46a319bd7a Randomize port assignemnt 2024-03-20 12:59:10 +01:00
durch af68da9406 Dont panic on error 2024-03-20 12:59:10 +01:00
durch 27978908d0 Disable metrics server for wasm 2024-03-20 12:59:10 +01:00
durch 72cffc71cc Light server to statistics control 2024-03-20 12:59:10 +01:00
Drazen 5753c30973 Instrument client-core 2024-03-20 12:59:10 +01:00
Drazen 7cbba823f8 metrics macros 2024-03-20 12:59:10 +01:00
Jon Häggblad c89273ab37 Update nym-wallet Cargo.lock 2024-03-20 10:53:09 +01:00
Jon Häggblad 6d66d769de Merge remote-tracking branch 'origin/develop' into jon/update-some-nym-prefixes 2024-03-20 10:51:58 +01:00
Jon Häggblad 70d37576f4 Add new constructor methods to IPR request/responses (#4486)
* Add function to create ping request

* Add more constructor methods
2024-03-20 10:49:24 +01:00
fmtabbara 01e4abc95f Update width of confirmation modal 2024-03-19 21:03:11 +00:00
fmtabbara 2195310d06 remove walletconnect (for now) 2024-03-19 18:20:17 +00:00
Drazen Urch 5f98364e6f Add Gateways to prom-targets (#4483) 2024-03-19 17:02:55 +01:00
fmtabbara b6cd9b2bae add leap wallet 2024-03-19 14:37:10 +00:00
fmtabbara 682db85268 try to fix rpc endpoint 2024-03-19 12:46:17 +00:00
Tommy Verrall 78930d82b2 Merge pull request #4474 from nymtech/jon/ipr-embedding
Support running both NR and IPR
2024-03-18 15:57:49 +00:00
Sachin Kamath 1ebb0c7daa don't use bigdecimal 2024-03-18 20:28:55 +05:30
mx ae6c80f0cd FFI share lib + initial uniffi-bindgen-go implementation (#4394)
* fixed rebase conflict with cargo.lock

* shared cleanup

* moved returncode to shared

* first pass at Go binding structure

* minor cleanup

* working on custom type udl

* trying to get LDFLAG script working

* commit before changing alias -> proper types

* converted CCallbacks from aliases to Struct

* cleanup comments

* temp

* push to share

* cleanup

* trait Lift not implemented for *const i8 issue

* test of refactor:
* move c-specific var casting out of shared/ into cpp/
* error returning in go/ over ffi boundary with uniffi

*  _internal functions ffi wrapper agnostic
* moved lang-specific type conversions to cpp / go bindings and out of
  shared
* got send_message working in c/c++ & go
* split out c/c++-specific types to mod

* cont. with making _internal fns lang agnostic
* working on final fn for C and shared (listening for incoming messages)

* fixed return err on listen_for_incoming

* got full example run running again after shared/ refactor

* removed unused struct

* code comments

* got first runthrough of go example code

* script cleanup

* clean up readme instructions

* clippy

* removed unused imports

* rustfmt

* Update sdk/ffi/go/README.md with link to example file

Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com>

* updated readme with extra build and usage info

* renamed binding outer directory for nicer path
* moved example file from ffi/main.go -> ./example.go

* updated README with new example file name

---------

Co-authored-by: mfahampshire <mfahampshire@pm.me>
Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com>
2024-03-18 15:15:59 +01:00
Jon Häggblad 0b49c74ac9 Remove unused function 2024-03-18 13:00:27 +01:00
Jon Häggblad 34be5abaf3 More minor naming fixes 2024-03-18 13:00:27 +01:00
Jon Häggblad 7f3c53e196 Rename struct 2024-03-18 13:00:27 +01:00
Jon Häggblad b710fbe524 Imports 2024-03-18 13:00:27 +01:00
Jon Häggblad 4c125792b2 Update mod.rs 2024-03-18 13:00:27 +01:00
Jon Häggblad 8e6215ecf4 Rename to embedded_clients 2024-03-18 13:00:27 +01:00
Jon Häggblad 7132e2dae5 Remove outdated comments 2024-03-18 13:00:27 +01:00
Jon Häggblad 79852d9dcd Set storage paths indepedently 2024-03-18 13:00:27 +01:00
Jon Häggblad 30413d7877 Add debug derive 2024-03-18 13:00:27 +01:00
Jon Häggblad 08ed7b42de Change to anyhow::Result at top-level 2024-03-18 13:00:27 +01:00
Jon Häggblad 6e8c0ad90e wip 2024-03-18 13:00:27 +01:00
Jon Häggblad 8f1f61e247 Remove cli conflicts for nr vs ipr 2024-03-18 13:00:27 +01:00
Jędrzej Stuczyński 8044ad5445 Bugfix/gateway registration (#4442)
* added timeout for gateway handshake messages

* make clients downgrade their protocol version if credentials are not being used

* method visibility
2024-03-18 12:52:46 +01:00
Tommy Verrall 9a4737acd0 Merge pull request #4482 from nymtech/update_prom_script
Support multiple envs
2024-03-18 09:50:35 +00:00
durch 8231bc1c73 Support multiple envs 2024-03-18 09:15:12 +01:00
dependabot[bot] c3af84a952 Bump follow-redirects in /wasm/node-tester/internal-dev
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-16 23:12:48 +00:00
dependabot[bot] 2172644f98 Bump follow-redirects from 1.15.4 to 1.15.6 in /wasm/client/internal-dev
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-16 23:10:50 +00:00
dependabot[bot] 393b67873d Bump follow-redirects in /wasm/mix-fetch/internal-dev
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-16 23:09:28 +00:00
fmtabbara 70b15c9226 test mainnet mixnodes 2024-03-15 21:08:14 +00:00
fmtabbara f7e6677878 update dotenv config 2024-03-15 16:28:01 +00:00
fmtabbara 73e87b2deb test wallet connect projectId 2024-03-15 16:17:02 +00:00
dependabot[bot] 2cf65b3694 Bump follow-redirects in /docker/typescript_client/upload_contract
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.9 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.9...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-15 14:58:42 +00:00
dependabot[bot] 7bc1b0dbcf Bump follow-redirects from 1.15.4 to 1.15.6
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-15 14:57:47 +00:00
dependabot[bot] 68bc4a59f7 Bump follow-redirects from 1.15.4 to 1.15.6 in /nym-api/tests
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-15 14:00:38 +00:00
dependabot[bot] 2bc8a76899 Bump follow-redirects from 1.15.4 to 1.15.6 in /testnet-faucet
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-15 14:00:33 +00:00
Drazen Urch 25053e5e8a Promethus is our friend (#4408)
* Generic prom wrapper idea

* Extend packet_statistics control with prom metrics

* Replace counters with Counters

* Add legacy mixnode api route

* fmt

* Sanitize metric names

* Format metrics

* Script to make prom targets

* More metrics

* Update script

* Make sure we dont panic in the future

* Remove fragile test

* Add metrics endpoint auth

* Remove per IP metrics

* Update target script, node_exporter setup

* Remove prom from client

* Simplify node stat

* Centralize metrice, break cpucycles temporarily

* Remove prometheus from mixnode

* Add cpu-cycles to Prom

* Further centralize Registry

* Cleanup old tracing

* Remove spurious assignment

* Move cpu-cycles to metrics

* Add features

* setup_logging before logging

* Remove cpucycle measurement in favour of time

* Cleanup, hygine
2024-03-15 14:59:52 +01:00
fmtabbara 6735c1ae58 append to env (dont overwrite) 2024-03-14 21:30:59 +00:00
fmtabbara b037c8fbeb test mobile delegations config 2024-03-14 14:15:41 +00:00
fmtabbara 3fcfbad4c0 add sandbox to cosmos-kit 2024-03-13 14:35:17 +00:00
Jędrzej Stuczyński da14947227 decreased logging level of gateway errors associated with the websocket 2024-03-13 11:32:57 +00:00
Jędrzej Stuczyński 5e40e480bc adjusting severity of logs for missing DKG contract in the gateway 2024-03-13 11:17:52 +00:00
Jędrzej Stuczyński 490319d961 making contract addresses optional in the env 2024-03-13 11:17:26 +00:00
Jędrzej Stuczyński 810adb82cc Merge pull request #4467 from nymtech/feature/extend-network-details-builder
allow setting whole chain details in a single method
2024-03-13 10:42:56 +00:00
Jędrzej Stuczyński 0e11cf92fc allow setting whole chain details in a single method 2024-03-13 10:42:33 +00:00
fmtabbara 43e4cabb85 Merge branch 'develop' into feature/explorer-delegation 2024-03-12 14:28:16 +00:00
fmtabbara 496a1bc97d package updates 2024-03-12 10:20:38 +00:00
fmtabbara 7db4ddff56 use url as unique nav selection state 2024-03-12 10:20:26 +00:00
fmtabbara 6b25f83ee3 small UI updates
tidy up some text layouts + allow mixnode row selections
2024-03-12 10:19:03 +00:00
fmtabbara 7602f4b130 create delegations context and page 2024-03-12 10:17:29 +00:00
fmtabbara f762062f9f create wallet context + wallet components
create wallet context using cosmoskit + include nymclient + create wallet components
2024-03-12 10:16:06 +00:00
Mark Sinclair a0958cddb4 Rework hash GitHub Action to be pre-bundled (#4462)
* Add released GitHub Action bundle

* Add settings from `owner` and `repo`

* fix typo

* Remove module type

* Move to subdir

* Publish with dependencies in bundle

* Change handling of version

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
Co-authored-by: pierre <dommerc.pierre@gmail.com>
2024-03-12 11:03:02 +01:00
Jon Häggblad 5bb9e36842 Tweak display impl for IpPair (#4454) 2024-03-11 18:03:39 +01:00
Jon Häggblad 0282251016 Add TaskStatus::ReadyWithGateway (#4449)
* Add TaskStatus::ReadyWithGateway

* Explicitly set starting status
2024-03-11 15:01:02 +01:00
import this 9b78409fdc Merge pull request #4446 from nymtech/serinko/guide/nym-vpn_0.0.5
[DOC]: NymVPN testing update and syntax automation
2024-03-11 10:46:00 +00:00
serinko f26d4ab882 final version with automated commands update 2024-03-08 23:47:03 +01:00
serinko ca86bbc3a5 troubleshoot all script issues - full auto mode now 2024-03-08 22:37:31 +01:00
serinko 1f41eca0b2 automate GUI script for latest version pull 2024-03-08 15:43:13 +01:00
Jon Häggblad 0e56d8c2f7 Add severity level to IPR response and downgrade filter check failed (#4447)
* Add info level to response from ipr

* Downgrade exit policy filter check failed to warning

* Bump ipr request response version
2024-03-08 15:21:41 +01:00
serinko c13297d18d implement nym-vpn version vars 2024-03-08 12:32:43 +01:00
serinko fe3c6bdad4 comment out qualitative testing 2024-03-07 19:24:21 +01:00
serinko 57b9372050 comment out qualitative testing 2024-03-07 19:22:05 +01:00
serinko 8371bf898f upgrade guide 0.0.5-dev -> 0.0.5 2024-03-07 19:09:19 +01:00
serinko aa5691447d update version, new script and simplify releases var 2024-03-07 13:00:46 +01:00
Jon Häggblad fa8e81d9dd Re-export Location type in explorer-client (#4445) 2024-03-06 17:26:51 +01:00
dependabot[bot] bc19fa7a78 Bump es5-ext in /sdk/typescript/packages/nodejs-client (#4434)
Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.62 to 0.10.64.
- [Release notes](https://github.com/medikoo/es5-ext/releases)
- [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md)
- [Commits](https://github.com/medikoo/es5-ext/compare/v0.10.62...v0.10.64)

---
updated-dependencies:
- dependency-name: es5-ext
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2024-03-06 15:11:32 +01:00
dependabot[bot] df1b648fa0 Bump es5-ext in /sdk/typescript/packages/mix-fetch-node (#4433)
Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.62 to 0.10.64.
- [Release notes](https://github.com/medikoo/es5-ext/releases)
- [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md)
- [Commits](https://github.com/medikoo/es5-ext/compare/v0.10.62...v0.10.64)

---
updated-dependencies:
- dependency-name: es5-ext
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2024-03-06 15:05:34 +01:00
dependabot[bot] 846fd6aeaa Bump mio from 0.8.10 to 0.8.11 in /sdk/ffi/cpp (#4443)
Bumps [mio](https://github.com/tokio-rs/mio) from 0.8.10 to 0.8.11.
- [Release notes](https://github.com/tokio-rs/mio/releases)
- [Changelog](https://github.com/tokio-rs/mio/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/mio/compare/v0.8.10...v0.8.11)

---
updated-dependencies:
- dependency-name: mio
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 14:50:44 +01:00
dependabot[bot] fbba59f001 Bump ip from 2.0.0 to 2.0.1 in /wasm/mix-fetch/internal-dev-node (#4419)
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2024-03-06 14:49:27 +01:00
dependabot[bot] b94c81a784 Bump ip from 2.0.0 to 2.0.1 in /wasm/client/internal-dev-node (#4418)
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2024-03-06 14:48:56 +01:00
dependabot[bot] 67b893175f Bump ip in /sdk/typescript/tests/integration-tests/mix-fetch (#4420)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2024-03-06 14:19:51 +01:00
Jon Häggblad a9ff4ccb10 Update imports 2024-03-06 10:40:37 +01:00
Jon Häggblad 2f4f303223 Add nym- prefix to http-api-client 2024-03-06 10:38:41 +01:00
Jon Häggblad 3265d58052 Add nym- prefix to ledger crate 2024-03-06 10:27:42 +01:00
Jon Häggblad 30facbcec4 Update module imports 2024-03-06 10:26:15 +01:00
Jon Häggblad 0f5157f7d1 Update dependency names 2024-03-06 10:24:21 +01:00
Jon Häggblad 3369aa51bc Add nym- prefix to async-file-watcher 2024-03-06 10:20:13 +01:00
dependabot[bot] 9e5890a0d7 Bump ip in /clients/native/examples/js-examples/websocket (#4421)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.5 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.5...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 08:15:03 +01:00
dependabot[bot] 3bda5f59a3 Bump ip from 2.0.0 to 2.0.1 (#4417)
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 07:58:31 +01:00
import this 154dfa089b [DOC]: Hotfix - remove unexisting page (#4438)
* [DOC]: Hotfix - remove unexisting page

* update modules and run build

* installed with nvm 18

* yarn version solved

* remove extra files

* attempt to resolve module versioning

* Update package versions and fix keplr example

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2024-03-05 15:07:38 +00:00
Jon Häggblad bd0cbbc18a Fix typo in macro invocation (#4441) 2024-03-04 14:48:43 +01:00
Jędrzej Stuczyński ed0e7a7a25 Merge pull request #4439 from nymtech/chore/move-nym-id
Chore/move nym
2024-03-04 12:19:41 +00:00
Jon Häggblad 5b35cfcfb2 build-information: pick up vergen from consuming crate (#4424) 2024-03-04 12:16:48 +01:00
Bogdan-Ștefan Neacşu d3ba008b88 Add IPPair constructor (#4440)
* Add IPPair constructor

* Rename
2024-03-04 12:56:10 +02:00
Jędrzej Stuczyński a04a782dbf renamed nym-id-lib to nym-id 2024-03-04 10:08:10 +00:00
Jędrzej Stuczyński f5d9fda0b1 moved and renamed nym-id to nym-id-cli 2024-03-04 09:38:29 +00:00
Bogdan-Ștefan Neacşu aebd386382 Add IPv6 support to IPPR (#4431) 2024-03-01 19:28:21 +02:00
Bogdan-Ștefan Neacşu 9a6f96b5e0 Fix windows build (#4426)
* Fix windows build

* Fix in another place too

* Install clang

* With sudo

* Revert "cargo update -p rustls@0.21.7 (#4404)"

This reverts commit ecc47cd418.
2024-03-01 12:07:38 +02:00
Jędrzej Stuczyński 5a3ff0f9f7 Merge pull request #4436 from nymtech/feature/nym-id-scaffold
Feature/nym id scaffold
2024-03-01 09:14:40 +00:00
Jędrzej Stuczyński 160db34651 clippy 2024-02-29 16:53:50 +00:00
Jędrzej Stuczyński ae20d2afb8 removed old test code 2024-02-29 16:04:07 +00:00
Jędrzej Stuczyński 41b7a2a20d cargo fmt 2024-02-29 15:57:48 +00:00
Jędrzej Stuczyński 208ec4574b using the shared code for credentials import 2024-02-29 15:29:41 +00:00
Jon Häggblad 2bff66e2c7 Remove rustls feature on workspace deps (#4422)
* Remove rustls feature on workspace deps

* Cargo.lock for nym-connect and nym-wallet
2024-02-28 18:45:00 +02:00
fmtabbara 28a0c3eb17 Add buffer module to webpack.common.js 2024-02-28 11:50:19 +00:00
Jędrzej Stuczyński 1aad5fc1bf created nym-id for importing credentials 2024-02-28 11:22:46 +00:00
fmtabbara a3d7b541e2 Fix currencyToString function call in Mixnodes page 2024-02-28 10:16:20 +00:00
Mark Sinclair 7861a0081a Add mixnet contract to env and add CI targets to package.json 2024-02-28 10:16:20 +00:00
Mark Sinclair c8b911d5d0 Make mixnet contract configurable 2024-02-28 10:16:20 +00:00
fmtabbara 22979ce245 add ledger intergration 2024-02-28 10:16:20 +00:00
fmtabbara b5a3a720a4 fix up delegate button in mobile view 2024-02-28 10:16:20 +00:00
fmtabbara 0ec5fb37c2 replace delegate icon button with button
fix linting
2024-02-28 10:16:20 +00:00
fmtabbara 2817c68e2e make mobile nav neater 2024-02-28 10:16:20 +00:00
fmtabbara c3050e5d02 allow clear search entry
fix react hooks error
2024-02-28 10:16:19 +00:00
fmtabbara 8f1901b6e0 tidy up mixnode and gateway tables 2024-02-28 10:16:19 +00:00
fmtabbara 96a42fa03d Fix table layouts
tidy up exports
2024-02-28 10:16:18 +00:00
fmtabbara 718de1f102 fix up mobile view
fix deps issues
2024-02-28 10:16:12 +00:00
fmtabbara cb9d730c16 display user message when wallet is not connected
fix linting
2024-02-28 10:16:12 +00:00
Yana 0e17fe5581 Add delegate button to each mixnode raw
relocate code

code refactor

wip

refactor

wip
2024-02-28 10:16:07 +00:00
Yana b25747dd60 Fix ModalError styles 2024-02-28 10:16:06 +00:00
Yana b16349f2e9 Add confirmation models 2024-02-28 10:16:06 +00:00
Yana f4b5693bcb Add CosmWasmSigningClient
WIP

wip
2024-02-28 10:16:06 +00:00
Yana 42a53a1c49 Remove identity key, mixId and amount validation
WIP
2024-02-28 10:16:06 +00:00
Yana 081fec72e5 Connect delegate modal to keplr balance
WIP

WIP

WIP

WIP
2024-02-28 10:16:06 +00:00
Yana f2223dddca Add Delegations Modal UI 2024-02-28 10:16:06 +00:00
Yana 9be7b8ab56 Add TokenSVG 2024-02-28 10:16:06 +00:00
Yana 40dc0d2cc7 fix ui bug in ts.config 2024-02-28 10:16:06 +00:00
Yana 19de5cb792 WIP
WIP
2024-02-28 10:16:06 +00:00
import this cb3e73fbd7 [DOC]/operators: Validator rewards (#4427)
* initialise token economics chapter

* initialise validator rewards page

* add todo points

* syntax edits

* docs: minor fixes

* add currency overview

* create bash scripts for nyx stake

* add nymvisor url

* final version of validator rewards

* final version of validator rewards

* final version of validator rewards

---------

Co-authored-by: Sachin Kamath <github@skamath.me>
2024-02-27 14:17:44 +00:00
Sachin Kamath 377e06daab nym-cli: clippy fixes 2024-02-22 18:10:41 +05:30
Sachin Kamath b54d82a01a nym-cli: batch transactions 2024-02-22 17:55:35 +05:30
Sachin Kamath ad052ef498 fmt + clippy 2024-02-21 19:14:12 +05:30
Sachin Kamath a3bc4af8fe fmt + clippy 2024-02-21 19:12:02 +05:30
Sachin Kamath b6d57e2862 nym-cli: delegate to mixnodes from input csv file
nym-cli: delegate to mixnodes from input csv file
2024-02-21 18:44:15 +05:30
750 changed files with 66584 additions and 11887 deletions
+10 -2
View File
@@ -14,12 +14,20 @@ inputs:
description: 'The tag/release to process. Uses the release id when trigger from a release.'
required: false
default: ''
repo:
description: 'The repo to use. Defaults to "nym".'
required: false
default: 'nym'
owner:
description: 'The repo owner to use. Defaults to "nymtech".'
required: false
default: 'nymtech'
outputs:
hashes:
description: 'A string containing JSON with the release asset hashes and signatures'
runs:
using: 'node16'
main: 'index.js'
using: 'node20'
main: 'dist/index.js'
branding:
icon: 'hash'
color: 'green'
@@ -0,0 +1,450 @@
export const id = 37;
export const ids = [37];
export const modules = {
/***/ 4037:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "toFormData": () => (/* binding */ toFormData)
/* harmony export */ });
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2777);
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8010);
let s = 0;
const S = {
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
END: s++
};
let f = 1;
const F = {
PART_BOUNDARY: f,
LAST_BOUNDARY: f *= 2
};
const LF = 10;
const CR = 13;
const SPACE = 32;
const HYPHEN = 45;
const COLON = 58;
const A = 97;
const Z = 122;
const lower = c => c | 0x20;
const noop = () => {};
class MultipartParser {
/**
* @param {string} boundary
*/
constructor(boundary) {
this.index = 0;
this.flags = 0;
this.onHeaderEnd = noop;
this.onHeaderField = noop;
this.onHeadersEnd = noop;
this.onHeaderValue = noop;
this.onPartBegin = noop;
this.onPartData = noop;
this.onPartEnd = noop;
this.boundaryChars = {};
boundary = '\r\n--' + boundary;
const ui8a = new Uint8Array(boundary.length);
for (let i = 0; i < boundary.length; i++) {
ui8a[i] = boundary.charCodeAt(i);
this.boundaryChars[ui8a[i]] = true;
}
this.boundary = ui8a;
this.lookbehind = new Uint8Array(this.boundary.length + 8);
this.state = S.START_BOUNDARY;
}
/**
* @param {Uint8Array} data
*/
write(data) {
let i = 0;
const length_ = data.length;
let previousIndex = this.index;
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
const boundaryLength = this.boundary.length;
const boundaryEnd = boundaryLength - 1;
const bufferLength = data.length;
let c;
let cl;
const mark = name => {
this[name + 'Mark'] = i;
};
const clear = name => {
delete this[name + 'Mark'];
};
const callback = (callbackSymbol, start, end, ui8a) => {
if (start === undefined || start !== end) {
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
}
};
const dataCallback = (name, clear) => {
const markSymbol = name + 'Mark';
if (!(markSymbol in this)) {
return;
}
if (clear) {
callback(name, this[markSymbol], i, data);
delete this[markSymbol];
} else {
callback(name, this[markSymbol], data.length, data);
this[markSymbol] = 0;
}
};
for (i = 0; i < length_; i++) {
c = data[i];
switch (state) {
case S.START_BOUNDARY:
if (index === boundary.length - 2) {
if (c === HYPHEN) {
flags |= F.LAST_BOUNDARY;
} else if (c !== CR) {
return;
}
index++;
break;
} else if (index - 1 === boundary.length - 2) {
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
state = S.END;
flags = 0;
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
index = 0;
callback('onPartBegin');
state = S.HEADER_FIELD_START;
} else {
return;
}
break;
}
if (c !== boundary[index + 2]) {
index = -2;
}
if (c === boundary[index + 2]) {
index++;
}
break;
case S.HEADER_FIELD_START:
state = S.HEADER_FIELD;
mark('onHeaderField');
index = 0;
// falls through
case S.HEADER_FIELD:
if (c === CR) {
clear('onHeaderField');
state = S.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) {
break;
}
if (c === COLON) {
if (index === 1) {
// empty header field
return;
}
dataCallback('onHeaderField', true);
state = S.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
return;
}
break;
case S.HEADER_VALUE_START:
if (c === SPACE) {
break;
}
mark('onHeaderValue');
state = S.HEADER_VALUE;
// falls through
case S.HEADER_VALUE:
if (c === CR) {
dataCallback('onHeaderValue', true);
callback('onHeaderEnd');
state = S.HEADER_VALUE_ALMOST_DONE;
}
break;
case S.HEADER_VALUE_ALMOST_DONE:
if (c !== LF) {
return;
}
state = S.HEADER_FIELD_START;
break;
case S.HEADERS_ALMOST_DONE:
if (c !== LF) {
return;
}
callback('onHeadersEnd');
state = S.PART_DATA_START;
break;
case S.PART_DATA_START:
state = S.PART_DATA;
mark('onPartData');
// falls through
case S.PART_DATA:
previousIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(data[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = data[i];
}
if (index < boundary.length) {
if (boundary[index] === c) {
if (index === 0) {
dataCallback('onPartData', true);
}
index++;
} else {
index = 0;
}
} else if (index === boundary.length) {
index++;
if (c === CR) {
// CR = part boundary
flags |= F.PART_BOUNDARY;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
flags |= F.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 === boundary.length) {
if (flags & F.PART_BOUNDARY) {
index = 0;
if (c === LF) {
// unset the PART_BOUNDARY flag
flags &= ~F.PART_BOUNDARY;
callback('onPartEnd');
callback('onPartBegin');
state = S.HEADER_FIELD_START;
break;
}
} else if (flags & F.LAST_BOUNDARY) {
if (c === HYPHEN) {
callback('onPartEnd');
state = S.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index - 1] = c;
} else if (previousIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
callback('onPartData', 0, previousIndex, _lookbehind);
previousIndex = 0;
mark('onPartData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case S.END:
break;
default:
throw new Error(`Unexpected state entered: ${state}`);
}
}
dataCallback('onHeaderField');
dataCallback('onHeaderValue');
dataCallback('onPartData');
// Update properties for the next call
this.index = index;
this.state = state;
this.flags = flags;
}
end() {
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
this.onPartEnd();
} else if (this.state !== S.END) {
throw new Error('MultipartParser.end(): stream ended unexpectedly');
}
}
}
function _fileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
if (!m) {
return;
}
const match = m[2] || m[3] || '';
let filename = match.slice(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
return String.fromCharCode(code);
});
return filename;
}
async function toFormData(Body, ct) {
if (!/multipart/i.test(ct)) {
throw new TypeError('Failed to fetch');
}
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (!m) {
throw new TypeError('no or bad content-type header, no multipart boundary');
}
const parser = new MultipartParser(m[1] || m[2]);
let headerField;
let headerValue;
let entryValue;
let entryName;
let contentType;
let filename;
const entryChunks = [];
const formData = new formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__/* .FormData */ .Ct();
const onPartData = ui8a => {
entryValue += decoder.decode(ui8a, {stream: true});
};
const appendToFile = ui8a => {
entryChunks.push(ui8a);
};
const appendFileToFormData = () => {
const file = new fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__/* .File */ .$B(entryChunks, filename, {type: contentType});
formData.append(entryName, file);
};
const appendEntryToFormData = () => {
formData.append(entryName, entryValue);
};
const decoder = new TextDecoder('utf-8');
decoder.decode();
parser.onPartBegin = function () {
parser.onPartData = onPartData;
parser.onPartEnd = appendEntryToFormData;
headerField = '';
headerValue = '';
entryValue = '';
entryName = '';
contentType = '';
filename = null;
entryChunks.length = 0;
};
parser.onHeaderField = function (ui8a) {
headerField += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderValue = function (ui8a) {
headerValue += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderEnd = function () {
headerValue += decoder.decode();
headerField = headerField.toLowerCase();
if (headerField === 'content-disposition') {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
if (m) {
entryName = m[2] || m[3] || '';
}
filename = _fileName(headerValue);
if (filename) {
parser.onPartData = appendToFile;
parser.onPartEnd = appendFileToFormData;
}
} else if (headerField === 'content-type') {
contentType = headerValue;
}
headerValue = '';
headerField = '';
};
for await (const chunk of Body) {
parser.write(chunk);
}
parser.end();
return formData;
}
/***/ })
};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,57 @@
'use strict';
const fs = require('fs');
const crypto = require('crypto');
const {parentPort} = require('worker_threads');
const handlers = {
hashFile: (algorithm, filePath) => new Promise((resolve, reject) => {
const hasher = crypto.createHash(algorithm);
fs.createReadStream(filePath)
// TODO: Use `Stream.pipeline` when targeting Node.js 12.
.on('error', reject)
.pipe(hasher)
.on('error', reject)
.on('finish', () => {
const {buffer} = new Uint8Array(hasher.read());
resolve({value: buffer, transferList: [buffer]});
});
}),
hash: async (algorithm, input) => {
const hasher = crypto.createHash(algorithm);
if (Array.isArray(input)) {
for (const part of input) {
hasher.update(part);
}
} else {
hasher.update(input);
}
const {buffer} = new Uint8Array(hasher.digest());
return {value: buffer, transferList: [buffer]};
}
};
parentPort.on('message', async message => {
try {
const {method, args} = message;
const handler = handlers[method];
if (handler === undefined) {
throw new Error(`Unknown method '${method}'`);
}
const {value, transferList} = await handler(...args);
parentPort.postMessage({id: message.id, value}, transferList);
} catch (error) {
const newError = {message: error.message, stack: error.stack};
for (const [key, value] of Object.entries(error)) {
if (typeof value !== 'object') {
newError[key] = value;
}
}
parentPort.postMessage({id: message.id, error: newError});
}
});
@@ -1,15 +0,0 @@
import core from "@actions/core";
import github from "@actions/github";
import { createHashesFromReleaseTagOrNameOrId } from './create-hashes.mjs';
const algorithm = core.getInput('hash-type');
const filename = core.getInput("file-name");
// use the release id from the payload if it is set
const releaseTagOrNameOrId = core.getInput("release-tag-or-name-or-id") || github.context.payload.release?.id;
try {
await createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm, filename })
} catch (error) {
core.setFailed(error.message);
}
+2 -13
View File
@@ -2,17 +2,6 @@
"name": "nym-hash-release",
"version": "1.0.0",
"description": "Generate hashes and signatures for assets in Nym releases",
"main": "index.js",
"type": "module",
"scripts": {
"local": "node run-local.mjs"
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@octokit/auth-action": "^4.0.0",
"@octokit/rest": "^20.0.1",
"hasha": "^5.2.0",
"node-fetch": "^3.2.10"
}
"main": "dist/index.js",
"type": "module"
}
@@ -1,6 +0,0 @@
import {createHashesFromReleaseTagOrNameOrId} from './create-hashes.mjs';
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 119065724, cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: '119065724', cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-connect-v1.1.19-snickers', cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'Nym Connect v1.1.19-snickers', cache: true, upload: false});
@@ -0,0 +1,14 @@
# nym-hash-release
This is the source code for the custom GitHub Action to calculate hashes.
It is in a subdirectory to avoid issues with `package.json`.
## Build
The following will bundle all code and dependencies into the `dist` folder, and copy it into place for GitHub Actions.
```
npm run build
npm run dist:copy
```
@@ -11,10 +11,14 @@ function getBinInfo(path) {
let mode = fs.statSync(path).mode
fs.chmodSync(path, mode | 0o111)
const raw = execSync(`${path} build-info --output=json`, { stdio: 'pipe', encoding: "utf8" });
const cmd = `${path} build-info --output=json`;
console.log(`🚚 Running ${cmd}... (for max of 3 seconds, then SIGTERM)`);
const raw = execSync(cmd, { stdio: 'pipe', encoding: "utf8", timeout: 3000 });
const parsed = JSON.parse(raw)
console.log(` ✅ ok`);
return parsed
} catch (_) {
console.log(` ❌ failed`);
return undefined
}
}
@@ -24,8 +28,11 @@ async function run(assets, algorithm, filename, cache) {
console.warn("cache is set to 'false', but we we no longer support it")
}
const directory = path.join(process.env.RUNNER_TEMP || '.tmp', process.env.GITHUB_RUN_ID || '');
console.log('Temporary directory: ', directory);
try {
fs.mkdirSync('.tmp');
fs.mkdirSync(directory, { recursive: true });
} catch(e) {
// ignore
}
@@ -40,13 +47,13 @@ async function run(assets, algorithm, filename, cache) {
let sig = null;
// cache in `${WORKING_DIR}/.tmp/`
const cacheFilename = path.resolve(`.tmp/${asset.name}`);
const cacheFilename = path.join(directory, `${asset.name}`);
if(!fs.existsSync(cacheFilename)) {
console.log(`Downloading ${asset.browser_download_url}... to ${cacheFilename}`);
console.log(`⬇️ Downloading ${asset.browser_download_url}... to ${cacheFilename} [${numAwaiting} of ${assets.length}]`);
buffer = Buffer.from(await fetch(asset.browser_download_url).then(res => res.arrayBuffer()));
fs.writeFileSync(cacheFilename, buffer);
} else {
console.log(`Loading from ${cacheFilename}`);
console.log(`💾 Loading from ${cacheFilename}`);
buffer = Buffer.from(fs.readFileSync(cacheFilename));
// console.log('Reading signature from content');
@@ -131,6 +138,7 @@ async function run(assets, algorithm, filename, cache) {
}
}
}
console.log(`Completed hashing ${assets.length} files`);
return hashes;
}
@@ -142,7 +150,7 @@ export async function createHashes({ assets, algorithm, filename, cache }) {
return output;
}
export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm = 'sha256', filename = 'hashes.json', cache = false, upload = true }) {
export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm = 'sha256', filename = 'hashes.json', cache = false, upload = true, owner = 'nymtech', repo = 'nym' }) {
console.log("🚀🚀🚀 Getting releases");
let auth;
@@ -157,8 +165,6 @@ export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrI
auth: process.env.GITHUB_TOKEN,
request: { fetch }
});
const owner = "nymtech";
const repo = "nym";
let releases;
if(cache) {
@@ -212,7 +218,14 @@ export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrI
releasesToProcess.forEach(release => {
const {tag_name, name} = release;
const tagComponents = tag_name.split('-v');
const matches = tag_name.match(/(\S+)-v([0-9]+\.[0-9]+(\.\S+)?)/);
if(!matches || matches.length < 2) {
console.warn('Could not match version structure in tag name = ', tag_name);
return;
}
const tagComponents = matches.slice(1);
const componentName = tagComponents[0];
const componentVersion = 'v' + tagComponents[1];
@@ -0,0 +1,21 @@
import core from "@actions/core";
import github from "@actions/github";
import { createHashesFromReleaseTagOrNameOrId } from './create-hashes.mjs';
const algorithm = core.getInput('hash-type');
const filename = core.getInput("file-name");
const owner = core.getInput("owner");
const repo = core.getInput("repo");
async function main() {
// use the release id from the payload if it is set
const releaseTagOrNameOrId = core.getInput("release-tag-or-name-or-id") || github.context.payload.release?.id;
try {
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId, algorithm, filename, owner, repo})
} catch (error) {
core.setFailed(error.message);
}
}
main().catch(error => core.setFailed(error.message));
@@ -1,26 +1,28 @@
{
"name": "ghaction-generate-release-hashes",
"name": "nym-hash-release",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghaction-generate-release-hashes",
"name": "nym-hash-release",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/core": "^1.10.1",
"@actions/github": "^5.1.1",
"@octokit/auth-action": "^4.0.0",
"@octokit/rest": "^20.0.1",
"@octokit/auth-action": "^4.0.1",
"@octokit/rest": "^20.0.2",
"hasha": "^5.2.0",
"node-fetch": "^3.2.10"
},
"devDependencies": {
"@vercel/ncc": "^0.38.1"
}
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz",
"integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
@@ -46,12 +48,12 @@
}
},
"node_modules/@octokit/auth-action": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-action/-/auth-action-4.0.0.tgz",
"integrity": "sha512-sMm9lWZdiX6e89YFaLrgE9EFs94k58BwIkvjOtozNWUqyTmsrnWFr/M5LolaRzZ7Kmb5FbhF9hi7FEeE274SoQ==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@octokit/auth-action/-/auth-action-4.0.1.tgz",
"integrity": "sha512-mJLOcFFafIivLZ7BEkGDCTFoHPJv7BeL5Zwy7j5qMDU0b/DKshhi6GCU9tw3vmKhOxTNquYfvwqsEfPpemaaxg==",
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/types": "^11.0.0"
"@octokit/types": "^12.0.0"
},
"engines": {
"node": ">= 18"
@@ -66,16 +68,16 @@
}
},
"node_modules/@octokit/auth-action/node_modules/@octokit/openapi-types": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz",
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
},
"node_modules/@octokit/auth-action/node_modules/@octokit/types": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz",
"integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==",
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dependencies": {
"@octokit/openapi-types": "^18.0.0"
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/auth-token": {
@@ -191,14 +193,14 @@
}
},
"node_modules/@octokit/rest": {
"version": "20.0.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.1.tgz",
"integrity": "sha512-wROV21RwHQIMNb2Dgd4+pY+dVy1Dwmp85pBrgr6YRRDYRBu9Gb+D73f4Bl2EukZSj5hInq2Tui9o7gAQpc2k2Q==",
"version": "20.0.2",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz",
"integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==",
"dependencies": {
"@octokit/core": "^5.0.0",
"@octokit/plugin-paginate-rest": "^8.0.0",
"@octokit/plugin-paginate-rest": "^9.0.0",
"@octokit/plugin-request-log": "^4.0.0",
"@octokit/plugin-rest-endpoint-methods": "^9.0.0"
"@octokit/plugin-rest-endpoint-methods": "^10.0.0"
},
"engines": {
"node": ">= 18"
@@ -261,17 +263,30 @@
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz",
"integrity": "sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==",
"version": "9.2.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz",
"integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==",
"dependencies": {
"@octokit/types": "^11.0.0"
"@octokit/types": "^12.6.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=5"
"@octokit/core": "5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": {
@@ -286,17 +301,30 @@
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-9.0.0.tgz",
"integrity": "sha512-KquMF/VB1IkKNiVnzJKspY5mFgGyLd7HzdJfVEGTJFzqu9BRFNWt+nwTCMuUiWc72gLQhRWYubTwOkQj+w/1PA==",
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
"integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
"dependencies": {
"@octokit/types": "^11.0.0"
"@octokit/types": "^12.6.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=5"
"@octokit/core": "5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/request": {
@@ -343,6 +371,15 @@
"@octokit/openapi-types": "^12.11.0"
}
},
"node_modules/@vercel/ncc": {
"version": "0.38.1",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.1.tgz",
"integrity": "sha512-IBBb+iI2NLu4VQn3Vwldyi2QwaXt5+hTyh58ggAMoCGE6DJmPvwL3KPBWcJl1m9LYPChBLE980Jw+CS4Wokqxw==",
"dev": true,
"bin": {
"ncc": "dist/ncc/cli.js"
}
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
@@ -0,0 +1,23 @@
{
"name": "nym-hash-release",
"version": "1.0.0",
"description": "Generate hashes and signatures for assets in Nym releases",
"main": "index.js",
"type": "module",
"scripts": {
"local": "node run-local.mjs",
"build": "ncc build index.js -o dist",
"dist:copy": "mkdir -p ../dist && cp dist/*.js ../dist"
},
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^5.1.1",
"@octokit/auth-action": "^4.0.1",
"@octokit/rest": "^20.0.2",
"hasha": "^5.2.0",
"node-fetch": "^3.2.10"
},
"devDependencies": {
"@vercel/ncc": "^0.38.1"
}
}
@@ -0,0 +1,11 @@
import {createHashesFromReleaseTagOrNameOrId} from './create-hashes.mjs';
const cache = true;
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-binaries-v2024.1-marabou', cache, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-vpn-desktop-v0.0.8', cache, upload: false, repo: 'nym-vpn-client'});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 119065724, cache: true, upload: false});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: '119065724', cache: true, upload: false});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-connect-v1.1.19-snickers', cache: true, upload: false});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'Nym Connect v1.1.19-snickers', cache: true, upload: false});
@@ -112,6 +112,7 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
retention-days: 30
# If this was a pull_request or nightly, upload to build server
@@ -130,6 +131,7 @@ jobs:
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-node $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
if [ ${{ github.event_name == 'workflow_dispatch' && inputs.enable_deb == true }} = true ]; then
@@ -60,7 +60,6 @@ jobs:
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ephemera.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
+3 -6
View File
@@ -8,8 +8,8 @@ on:
required: true
type: string
workflow_dispatch:
release_tag:
tag:
inputs:
release_tag:
description: 'Release tag'
required: true
type: string
@@ -24,10 +24,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install packages
run: cd ./.github/actions/nym-hash-releases && npm i
- uses: ./.github/actions/nym-hash-releases
- uses: nymtech/nym/.github/actions/nym-hash-releases@develop
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
Generated
+625 -206
View File
File diff suppressed because it is too large Load Diff
+40 -18
View File
@@ -24,6 +24,9 @@ members = [
"common/bandwidth-controller",
"common/bin-common",
"common/client-core",
"common/client-core/config-types",
"common/client-core/surb-storage",
"common/client-core/gateways-storage",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
@@ -32,13 +35,14 @@ members = [
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
# "common/cosmwasm-smart-contracts/ephemera",
# "common/cosmwasm-smart-contracts/ephemera",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/name-service",
"common/cosmwasm-smart-contracts/service-provider-directory",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/country-group",
"common/credential-storage",
"common/credentials",
"common/credential-utils",
@@ -48,6 +52,7 @@ members = [
"common/execute",
"common/exit-policy",
"common/http-api-client",
"common/http-api-common",
"common/inclusion-probability",
"common/ip-packet-requests",
"common/ledger",
@@ -56,6 +61,8 @@ members = [
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
"common/nymcoconut",
"common/nym-id",
"common/nym-metrics",
"common/nymsphinx",
"common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing",
@@ -100,17 +107,19 @@ members = [
"nym-browser-extension/storage",
"nym-api/nym-api-requests",
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-validator-rewarder",
"tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump",
"tools/nym-cli",
"tools/nym-id-cli",
"tools/nym-nr-query",
"tools/nymvisor",
"tools/ts-rs-cli",
"wasm/client",
# "wasm/full-nym-wasm",
# "wasm/full-nym-wasm",
"wasm/mix-fetch",
"wasm/node-tester",
]
@@ -126,9 +135,19 @@ default-members = [
"tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
"nym-node"
]
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles", "sdk/ffi/cpp"]
exclude = [
"explorer",
"contracts",
"nym-wallet",
"nym-connect/mobile/src-tauri",
"nym-connect/desktop",
"nym-vpn/ui/src-tauri",
"cpu-cycles",
"sdk/ffi/cpp",
]
[workspace.package]
authors = ["Nym Technologies SA"]
@@ -152,6 +171,7 @@ dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7"
getrandom = "0.2.10"
humantime-serde = "1.1.1"
hyper = "0.14.27"
k256 = "0.13"
lazy_static = "1.4.0"
@@ -159,7 +179,7 @@ log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = { version = "0.11.22", default_features = false, features = ["rustls-tls"] }
reqwest = { version = "0.11.22", default-features = false }
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
@@ -169,46 +189,48 @@ time = "0.3.30"
thiserror = "1.0.48"
tokio = "1.33.0"
tokio-util = "0.7.10"
tokio-tungstenite = { version = "0.20.1", features = ["rustls"] }
tokio-tungstenite = { version = "0.20.1" }
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false, features = ["rustls"] }
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
utoipa = "3.5.0"
utoipa-swagger-ui = "3.1.5"
url = "2.4"
zeroize = "1.6.0"
prometheus = { version = "0.13.0" }
# coconut/DKG related
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="feature/gt-serialization-0.8.0" }
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch = "feature/gt-serialization-0.8.0" }
group = "0.13.0"
ff = "0.13.0"
# cosmwasm-related
cosmwasm-derive = "=1.3.0"
cosmwasm-schema = "=1.3.0"
cosmwasm-std = "=1.3.0"
# use 0.5.0 as that's the version used by cosmwasm-std 1.3.0
cosmwasm-derive = "=1.4.3"
cosmwasm-schema = "=1.4.3"
cosmwasm-std = "=1.4.3"
# use 0.5.0 as that's the version used by cosmwasm-std 1.4.3
# (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=0.5.0"
cosmwasm-storage = "=1.3.0"
cosmwasm-storage = "=1.4.3"
# same version as used by cosmwasm
cw-utils = "=1.0.1"
cw-storage-plus = "=1.1.0"
cw2 = { version = "=1.1.0" }
cw3 = { version = "=1.1.0" }
cw4 = { version = "=1.1.0" }
cw-storage-plus = "=1.2.0"
cw2 = { version = "=1.1.2" }
cw3 = { version = "=1.1.2" }
cw4 = { version = "=1.1.2" }
cw-controllers = { version = "=1.1.0" }
# cosmrs-related
bip32 = "0.5.1"
# temporarily using a fork again (yay.) because we need staking and slashing support
cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch ="nym-temp/all-validator-features" }
cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" }
#cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" } # unfortuntely we need a fork by yours truly to get the staking support
tendermint = "0.34" # same version as used by cosmrs
tendermint = "0.34" # same version as used by cosmrs
tendermint-rpc = "0.34" # same version as used by cosmrs
prost = "0.12"
+3 -2
View File
@@ -4,7 +4,7 @@ version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
rust-version = "1.65"
rust-version = "1.70"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -38,7 +38,7 @@ zeroize = { workspace = true }
## internal
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "cli"] }
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
nym-config = { path = "../../common/config" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" }
@@ -51,5 +51,6 @@ nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
nym-client-websocket-requests = { path = "websocket-requests" }
nym-id = { path = "../../common/nym-id" }
[dev-dependencies]
+325 -125
View File
@@ -674,13 +674,13 @@
}
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dev": true,
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@@ -688,7 +688,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@@ -793,13 +793,19 @@
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1030,18 +1036,18 @@
}
},
"node_modules/content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true,
"engines": {
"node": ">= 0.6"
@@ -1137,6 +1143,23 @@
"node": ">= 10"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -1358,6 +1381,27 @@
"node": ">=4"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-module-lexer": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
@@ -1463,17 +1507,17 @@
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dev": true,
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@@ -1667,9 +1711,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true,
"funding": [
{
@@ -1705,9 +1749,9 @@
}
},
"node_modules/fs-monkey": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz",
"integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==",
"dev": true
},
"node_modules/fs.realpath": {
@@ -1731,20 +1775,28 @@
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1824,6 +1876,18 @@
"node": ">=0.10.0"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.1.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/graceful-fs": {
"version": "4.2.9",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
@@ -1855,10 +1919,22 @@
"node": ">=8"
}
},
"node_modules/has-symbols": {
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"dev": true,
"engines": {
"node": ">= 0.4"
@@ -1867,6 +1943,30 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -2160,9 +2260,9 @@
}
},
"node_modules/ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==",
"dev": true
},
"node_modules/ipaddr.js": {
@@ -2430,12 +2530,12 @@
}
},
"node_modules/memfs": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"dev": true,
"dependencies": {
"fs-monkey": "1.0.3"
"fs-monkey": "^1.0.4"
},
"engines": {
"node": ">= 4.0.0"
@@ -2650,9 +2750,9 @@
}
},
"node_modules/object-inspect": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
"integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -3038,9 +3138,9 @@
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dev": true,
"dependencies": {
"bytes": "3.1.2",
@@ -3413,6 +3513,23 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -3453,14 +3570,18 @@
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -4047,13 +4168,13 @@
}
},
"node_modules/webpack-dev-middleware": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
"dev": true,
"dependencies": {
"colorette": "^2.0.10",
"memfs": "^3.4.1",
"memfs": "^3.4.3",
"mime-types": "^2.1.31",
"range-parser": "^1.2.1",
"schema-utils": "^4.0.0"
@@ -5030,13 +5151,13 @@
"dev": true
},
"body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dev": true,
"requires": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@@ -5044,7 +5165,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@@ -5122,13 +5243,16 @@
"dev": true
},
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
}
},
"camel-case": {
@@ -5294,15 +5418,15 @@
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true
},
"cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true
},
"cookie-signature": {
@@ -5377,6 +5501,17 @@
"execa": "^5.0.0"
}
},
"define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"requires": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
}
},
"define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -5546,6 +5681,21 @@
"integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
"dev": true
},
"es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dev": true,
"requires": {
"get-intrinsic": "^1.2.4"
}
},
"es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true
},
"es-module-lexer": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
@@ -5626,17 +5776,17 @@
}
},
"express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dev": true,
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@@ -5800,9 +5950,9 @@
}
},
"follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true
},
"forwarded": {
@@ -5818,9 +5968,9 @@
"dev": true
},
"fs-monkey": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz",
"integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==",
"dev": true
},
"fs.realpath": {
@@ -5837,20 +5987,22 @@
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true
},
"get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
}
},
"get-stream": {
@@ -5908,6 +6060,15 @@
}
}
},
"gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"requires": {
"get-intrinsic": "^1.1.3"
}
},
"graceful-fs": {
"version": "4.2.9",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
@@ -5933,12 +6094,36 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"has-symbols": {
"has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"requires": {
"es-define-property": "^1.0.0"
}
},
"has-proto": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"dev": true
},
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dev": true
},
"hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"requires": {
"function-bind": "^1.1.2"
}
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -6157,9 +6342,9 @@
"dev": true
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==",
"dev": true
},
"ipaddr.js": {
@@ -6346,12 +6531,12 @@
"dev": true
},
"memfs": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"dev": true,
"requires": {
"fs-monkey": "1.0.3"
"fs-monkey": "^1.0.4"
}
},
"merge-descriptors": {
@@ -6515,9 +6700,9 @@
"dev": true
},
"object-inspect": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
"integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"dev": true
},
"object-is": {
@@ -6803,9 +6988,9 @@
"dev": true
},
"raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dev": true,
"requires": {
"bytes": "3.1.2",
@@ -7090,6 +7275,20 @@
"send": "0.18.0"
}
},
"set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
"requires": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
}
},
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -7121,14 +7320,15 @@
"dev": true
},
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
}
},
"signal-exit": {
@@ -7547,13 +7747,13 @@
}
},
"webpack-dev-middleware": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
"dev": true,
"requires": {
"colorette": "^2.0.10",
"memfs": "^3.4.1",
"memfs": "^3.4.3",
"mime-types": "^2.1.31",
"range-parser": "^1.2.1",
"schema-utils": "^4.0.0"
+4 -3
View File
@@ -4,7 +4,7 @@
use crate::client::config::persistence::ClientPaths;
use crate::client::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::cli_helpers::client_init::ClientConfig;
use nym_client_core::cli_helpers::CliClientConfig;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use nym_config::{
@@ -19,11 +19,12 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
pub use nym_client_core::config::DebugConfig;
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_33;
mod persistence;
mod template;
@@ -74,7 +75,7 @@ impl NymConfigTemplate for Config {
}
}
impl ClientConfig for Config {
impl CliClientConfig for Config {
fn common_paths(&self) -> &CommonClientPaths {
&self.storage_paths.common_paths
}
@@ -5,8 +5,8 @@ use crate::client::config::old_config_v1_1_20_2::{
ClientPathsV1_1_20_2, ConfigV1_1_20_2, SocketTypeV1_1_20_2, SocketV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::disk_persistence::old_v1_1_33::ClientKeysPathsV1_1_33;
use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::old_config_v1_1_20_2::{
ClientV1_1_20_2, ConfigV1_1_20_2 as BaseConfigV1_1_20_2,
@@ -60,7 +60,7 @@ impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
socket: value.socket.into(),
storage_paths: ClientPathsV1_1_20_2 {
common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths {
keys: ClientKeysPathsV1_1_33 {
private_identity_key_file: value.base.client.private_identity_key_file,
public_identity_key_file: value.base.client.public_identity_key_file,
private_encryption_key_file: value.base.client.private_encryption_key_file,
@@ -1,18 +1,15 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
client::config::{
default_config_filepath, persistence::ClientPaths, Config, Socket, SocketType,
},
error::ClientError,
use crate::client::config::old_config_v1_1_33::{
ClientPathsV1_1_33, ConfigV1_1_33, SocketTypeV1_1_33, SocketV1_1_33,
};
use crate::{client::config::default_config_filepath, error::ClientError};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseConfigV1_1_20_2;
use nym_client_core::config::old_config_v1_1_30::ConfigV1_1_30 as BaseConfigV1_1_30;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::config::old_config_v1_1_33::OldGatewayEndpointConfigV1_1_33;
use nym_config::read_config_from_toml_file;
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use serde::{Deserialize, Serialize};
@@ -49,12 +46,12 @@ impl ConfigV1_1_20_2 {
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), ClientError> {
pub fn upgrade(self) -> Result<(ConfigV1_1_33, OldGatewayEndpointConfigV1_1_33), ClientError> {
let gateway_details = self.base.client.gateway_endpoint.clone().into();
let config = Config {
let config = ConfigV1_1_33 {
base: BaseConfigV1_1_30::from(self.base).into(),
socket: self.socket.into(),
storage_paths: ClientPaths {
storage_paths: ClientPathsV1_1_33 {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
@@ -71,11 +68,11 @@ pub enum SocketTypeV1_1_20_2 {
None,
}
impl From<SocketTypeV1_1_20_2> for SocketType {
impl From<SocketTypeV1_1_20_2> for SocketTypeV1_1_33 {
fn from(value: SocketTypeV1_1_20_2) -> Self {
match value {
SocketTypeV1_1_20_2::WebSocket => SocketType::WebSocket,
SocketTypeV1_1_20_2::None => SocketType::None,
SocketTypeV1_1_20_2::WebSocket => SocketTypeV1_1_33::WebSocket,
SocketTypeV1_1_20_2::None => SocketTypeV1_1_33::None,
}
}
}
@@ -88,9 +85,9 @@ pub struct SocketV1_1_20_2 {
pub listening_port: u16,
}
impl From<SocketV1_1_20_2> for Socket {
impl From<SocketV1_1_20_2> for SocketV1_1_33 {
fn from(value: SocketV1_1_20_2) -> Self {
Socket {
SocketV1_1_33 {
socket_type: value.socket_type.into(),
host: value.host,
listening_port: value.listening_port,
@@ -0,0 +1,99 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths;
use crate::client::config::{default_config_filepath, Config, Socket, SocketType};
use crate::error::ClientError;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_33::CommonClientPathsV1_1_33;
use nym_client_core::config::old_config_v1_1_33::ConfigV1_1_33 as BaseConfigV1_1_33;
use nym_config::read_config_from_toml_file;
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use serde::{Deserialize, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr};
use std::path::Path;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct ClientPathsV1_1_33 {
#[serde(flatten)]
pub common_paths: CommonClientPathsV1_1_33,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct ConfigV1_1_33 {
#[serde(flatten)]
pub base: BaseConfigV1_1_33,
pub socket: SocketV1_1_33,
// \/ CHANGED
pub storage_paths: ClientPathsV1_1_33,
// /\ CHANGED
pub logging: LoggingSettings,
}
impl ConfigV1_1_33 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, ClientError> {
Ok(Config {
base: self.base.into(),
socket: self.socket.into(),
storage_paths: ClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
})
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketTypeV1_1_33 {
WebSocket,
None,
}
impl From<SocketTypeV1_1_33> for SocketType {
fn from(value: SocketTypeV1_1_33) -> Self {
match value {
SocketTypeV1_1_33::WebSocket => SocketType::WebSocket,
SocketTypeV1_1_33::None => SocketType::None,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct SocketV1_1_33 {
pub socket_type: SocketTypeV1_1_33,
pub host: IpAddr,
pub listening_port: u16,
}
impl From<SocketV1_1_33> for Socket {
fn from(value: SocketV1_1_33) -> Self {
Socket {
socket_type: value.socket_type.into(),
host: value.host,
listening_port: value.listening_port,
}
}
}
impl Default for SocketV1_1_33 {
fn default() -> Self {
SocketV1_1_33 {
socket_type: SocketTypeV1_1_33::WebSocket,
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
}
}
}
+3 -7
View File
@@ -50,10 +50,6 @@ keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key
# Path to file containing public encryption key.
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
@@ -64,9 +60,9 @@ credentials_database = '{{ storage_paths.credentials_database }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# Path to the file containing information about gateway used by this client,
# i.e. details such as its public key, owner address or the network information.
gateway_details = '{{ storage_paths.gateway_details }}'
# Path to the file containing information about gateways used by this client,
# i.e. details such as their public keys, owner addresses or the network information.
gateway_registrations = '{{ storage_paths.gateway_registrations }}'
##### socket config options #####
@@ -0,0 +1,30 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::cli_helpers::client_add_gateway::{add_gateway, CommonClientAddGatewayArgs};
#[derive(clap::Args)]
pub(crate) struct Args {
#[command(flatten)]
common_args: CommonClientAddGatewayArgs,
#[arg(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
impl AsRef<CommonClientAddGatewayArgs> for Args {
fn as_ref(&self) -> &CommonClientAddGatewayArgs {
&self.common_args
}
}
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
let output = args.output;
let res = add_gateway::<CliNativeClient, _>(args).await?;
println!("{}", output.format(&res));
Ok(())
}
@@ -1,101 +1,14 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use clap::ArgGroup;
use log::{error, info};
use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use std::fs;
use std::path::PathBuf;
use zeroize::Zeroizing;
use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportCredentialArgs,
};
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(clap::Args)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub(crate) struct Args {
/// Id of client that is going to import the credential
#[clap(long)]
pub id: String,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true, default_value_t = 1)]
pub(crate) version: u8,
}
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
let config = try_load_current_config(&args.id)?;
let credentials_store = nym_credential_storage::initialise_persistent_storage(
&config.storage_paths.common_paths.credentials_database,
)
.await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
let raw_credential = Zeroizing::new(raw_credential);
// we're unpacking the data in order to make sure it's valid
// and to extract relevant metadata for storage purposes
let credential = match args.version {
1 => Zeroizing::new(
IssuedBandwidthCredential::unpack_v1(&raw_credential).map_err(|source| {
ClientError::CredentialDeserializationFailure {
storage_revision: 1,
source,
}
})?,
),
other => panic!("unknown credential serialization version {other}"),
};
info!("importing {}", credential.typ());
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
info!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
info!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
error!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(ClientError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
let storable = StorableIssuedCredential {
serialization_revision: args.version,
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store.insert_issued_credential(storable).await?;
pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> {
import_credential::<CliNativeClient, _>(args).await?;
println!("successfully imported credential!");
Ok(())
}
+3 -12
View File
@@ -4,7 +4,7 @@
use crate::client::config::{
default_config_directory, default_config_filepath, default_data_directory,
};
use crate::commands::try_upgrade_config;
use crate::commands::CliNativeClient;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
@@ -21,17 +21,8 @@ use std::fs;
use std::net::IpAddr;
use std::path::PathBuf;
struct NativeClientInit;
impl InitialisableClient for NativeClientInit {
const NAME: &'static str = "native";
type Error = ClientError;
impl InitialisableClient for CliNativeClient {
type InitArgs = Init;
type Config = Config;
fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> {
try_upgrade_config(id)
}
fn initialise_storage_paths(id: &str) -> Result<(), Self::Error> {
fs::create_dir_all(default_data_directory(id))?;
@@ -124,7 +115,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> {
eprintln!("Initialising client...");
let output = args.output;
let res = initialise_client::<NativeClientInit>(args).await?;
let res = initialise_client::<CliNativeClient>(args).await?;
let init_results = InitResults::new(res);
println!("{}", output.format(&init_results));
@@ -0,0 +1,32 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::cli_helpers::client_list_gateways::{
list_gateways, CommonClientListGatewaysArgs,
};
#[derive(clap::Args)]
pub(crate) struct Args {
#[command(flatten)]
common_args: CommonClientListGatewaysArgs,
#[arg(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
impl AsRef<CommonClientListGatewaysArgs> for Args {
fn as_ref(&self) -> &CommonClientListGatewaysArgs {
&self.common_args
}
}
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
let output = args.output;
let res = list_gateways::<CliNativeClient, _>(args).await?;
println!("{}", output.format(&res));
Ok(())
}
+100 -45
View File
@@ -4,6 +4,7 @@
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::client::config::{BaseClientConfig, Config};
use crate::error::ClientError;
use clap::CommandFactory;
@@ -11,21 +12,37 @@ use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_config::OptionalSet;
use std::error::Error;
use std::net::IpAddr;
use std::sync::OnceLock;
mod add_gateway;
pub(crate) mod build_info;
pub(crate) mod import_credential;
pub(crate) mod init;
mod list_gateways;
pub(crate) mod run;
mod switch_gateway;
pub(crate) struct CliNativeClient;
impl CliClient for CliNativeClient {
const NAME: &'static str = "native";
type Error = ClientError;
type Config = Config;
async fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> {
try_upgrade_config(id).await
}
async fn try_load_current_config(id: &str) -> Result<Self::Config, Self::Error> {
try_load_current_config(id).await
}
}
fn pretty_build_info_static() -> &'static str {
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
@@ -56,7 +73,16 @@ pub(crate) enum Commands {
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(import_credential::Args),
ImportCredential(CommonClientImportCredentialArgs),
/// List all registered with gateways
ListGateways(list_gateways::Args),
/// Add new gateway to this client
AddGateway(add_gateway::Args),
/// Change the currently active gateway. Note that you must have already registered with the new gateway!
SwitchGateway(switch_gateway::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -87,6 +113,9 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
@@ -122,29 +151,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
}
fn persist_gateway_details(
config: &Config,
details: GatewayEndpointConfig,
) -> Result<(), ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
})
}
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.13 (which is incompatible with the next step, i.e. 1.1.19)
@@ -158,14 +165,22 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
let updated_step1: ConfigV1_1_20 = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated, gateway_config) = updated_step2.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let (updated_step3, gateway_config) = updated_step2.upgrade()?;
let old_paths = updated_step3.storage_paths.clone();
let updated = updated_step3.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
async fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
@@ -178,14 +193,21 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated, gateway_config) = updated_step1.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let (updated_step2, gateway_config) = updated_step1.upgrade()?;
let old_paths = updated_step2.storage_paths.clone();
let updated = updated_step2.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
async fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
@@ -195,28 +217,62 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
info!("It seems the client is using <= v1.1.20_2 config template.");
info!("It is going to get updated to the current specification.");
let (updated, gateway_config) = old_config.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let (updated_step1, gateway_config) = old_config.upgrade()?;
let old_paths = updated_step1.storage_paths.clone();
let updated = updated_step1.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
updated.save_to_default_location()?;
Ok(true)
}
async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, ClientError> {
// explicitly load it as v1.1.33 (which is incompatible with the current one, i.e. +1.1.34)
let Ok(old_config) = ConfigV1_1_33::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.33 config template.");
info!("It is going to get updated to the current specification.");
let old_paths = old_config.storage_paths.clone();
let updated = old_config.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
None,
)
.await?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
if try_upgrade_v1_1_13_config(id)? {
async fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
if try_upgrade_v1_1_13_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_20_config(id)? {
if try_upgrade_v1_1_20_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
if try_upgrade_v1_1_20_2_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_33_config(id).await? {
return Ok(());
}
Ok(())
}
fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
async fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
// try to load the config as is
if let Ok(cfg) = Config::read_from_default_path(id) {
return if !cfg.validate() {
@@ -227,7 +283,7 @@ fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
}
// we couldn't load it - try upgrading it from older revisions
try_upgrade_config(id)?;
try_upgrade_config(id).await?;
let config = match Config::read_from_default_path(id) {
Ok(cfg) => cfg,
@@ -247,7 +303,6 @@ fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_cli() {
+1 -1
View File
@@ -69,7 +69,7 @@ fn version_check(cfg: &Config) -> bool {
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("Starting client {}...", args.common_args.id);
let mut config = try_load_current_config(&args.common_args.id)?;
let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
@@ -0,0 +1,24 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_switch_gateway::{
switch_gateway, CommonClientSwitchGatewaysArgs,
};
#[derive(clap::Args, Clone, Debug)]
pub struct Args {
#[command(flatten)]
common_args: CommonClientSwitchGatewaysArgs,
}
impl AsRef<CommonClientSwitchGatewaysArgs> for Args {
fn as_ref(&self) -> &CommonClientSwitchGatewaysArgs {
&self.common_args
}
}
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
switch_gateway::<CliNativeClient, _>(args).await
}
+6 -18
View File
@@ -1,6 +1,6 @@
use nym_client_core::error::ClientCoreError;
use nym_credential_storage::error::StorageError;
use time::OffsetDateTime;
use nym_id::NymIdError;
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
@@ -23,21 +23,9 @@ pub enum ClientError {
#[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode,
#[error("failed to store credential: {source}")]
CredentialStorageFailure {
#[from]
source: StorageError,
},
#[error(transparent)]
ConfigUpgradeFailure(#[from] nym_client_core::config::ConfigUpgradeFailure),
#[error(
"failed to deserialize provided credential using revision {storage_revision}: {source}"
)]
CredentialDeserializationFailure {
storage_revision: u8,
#[source]
source: nym_credentials::error::Error,
},
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
@@ -8,7 +8,7 @@ use crate::error::{self, ErrorKind};
use crate::text::ClientRequestText;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE};
use std::convert::{TryFrom, TryInto};
use std::mem::size_of;
#[repr(u8)]
@@ -9,7 +9,7 @@ use crate::text::ServerResponseText;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE};
use nym_sphinx::receiver::ReconstructedMessage;
use std::convert::TryInto;
use std::mem::size_of;
#[repr(u8)]
@@ -7,7 +7,6 @@ use crate::responses::ServerResponse;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
// local text equivalent of `ClientRequest` for easier serialization + deserialization with serde
// TODO: figure out if there's an easy way to avoid defining it
+3 -2
View File
@@ -4,7 +4,7 @@ version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
license.workspace = true
[dependencies]
@@ -23,7 +23,7 @@ zeroize = { workspace = true }
# internal
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "cli"] }
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
@@ -35,6 +35,7 @@ nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-pemstore = { path = "../../common/pemstore" }
nym-topology = { path = "../../common/topology" }
nym-socks5-client-core = { path = "../../common/socks5-client-core" }
nym-id = { path = "../../common/nym-id" }
[features]
default = []
@@ -0,0 +1,30 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::cli_helpers::client_add_gateway::{add_gateway, CommonClientAddGatewayArgs};
#[derive(clap::Args)]
pub(crate) struct Args {
#[command(flatten)]
common_args: CommonClientAddGatewayArgs,
#[arg(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
impl AsRef<CommonClientAddGatewayArgs> for Args {
fn as_ref(&self) -> &CommonClientAddGatewayArgs {
&self.common_args
}
}
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let output = args.output;
let res = add_gateway::<CliSocks5Client, _>(args).await?;
println!("{}", output.format(&res));
Ok(())
}
@@ -1,101 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use clap::ArgGroup;
use log::{error, info};
use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use std::fs;
use std::path::PathBuf;
use zeroize::Zeroizing;
use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportCredentialArgs,
};
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(clap::Args)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub(crate) struct Args {
/// Id of client that is going to import the credential
#[clap(long)]
pub id: String,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true, default_value_t = 1)]
pub(crate) version: u8,
}
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let config = try_load_current_config(&args.id)?;
let credentials_store = nym_credential_storage::initialise_persistent_storage(
&config.storage_paths.common_paths.credentials_database,
)
.await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
let raw_credential = Zeroizing::new(raw_credential);
// we're unpacking the data in order to make sure it's valid
// and to extract relevant metadata for storage purposes
let credential = match args.version {
1 => Zeroizing::new(
IssuedBandwidthCredential::unpack_v1(&raw_credential).map_err(|source| {
Socks5ClientError::CredentialDeserializationFailure {
storage_revision: 1,
source,
}
})?,
),
other => panic!("unknown credential serialization version {other}"),
};
info!("importing {}", credential.typ());
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
info!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
info!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
error!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(Socks5ClientError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
let storable = StorableIssuedCredential {
serialization_revision: args.version,
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store.insert_issued_credential(storable).await?;
pub(crate) async fn execute(
args: CommonClientImportCredentialArgs,
) -> Result<(), Socks5ClientError> {
import_credential::<CliSocks5Client, _>(args).await?;
println!("successfully imported credential!");
Ok(())
}
+4 -13
View File
@@ -1,7 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_config;
use crate::commands::CliSocks5Client;
use crate::config::{
default_config_directory, default_config_filepath, default_data_directory, Config,
};
@@ -21,17 +21,8 @@ use std::fs;
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
struct Socks5ClientInit;
impl InitialisableClient for Socks5ClientInit {
const NAME: &'static str = "socks5";
type Error = Socks5ClientError;
impl InitialisableClient for CliSocks5Client {
type InitArgs = Init;
type Config = Config;
fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> {
try_upgrade_config(id)
}
fn initialise_storage_paths(id: &str) -> Result<(), Self::Error> {
fs::create_dir_all(default_data_directory(id))?;
@@ -118,7 +109,7 @@ impl InitResults {
Self {
client_address: res.init_results.address.to_string(),
client_core: res.init_results,
socks5_listening_address: res.config.core.socks5.bind_adddress,
socks5_listening_address: res.config.core.socks5.bind_address,
}
}
}
@@ -139,7 +130,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> {
eprintln!("Initialising client...");
let output = args.output;
let res = initialise_client::<Socks5ClientInit>(args).await?;
let res = initialise_client::<CliSocks5Client>(args).await?;
let init_results = InitResults::new(res);
println!("{}", output.format(&init_results));
@@ -0,0 +1,32 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::cli_helpers::client_list_gateways::{
list_gateways, CommonClientListGatewaysArgs,
};
#[derive(clap::Args)]
pub(crate) struct Args {
#[command(flatten)]
common_args: CommonClientListGatewaysArgs,
#[arg(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
impl AsRef<CommonClientListGatewaysArgs> for Args {
fn as_ref(&self) -> &CommonClientListGatewaysArgs {
&self.common_args
}
}
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let output = args.output;
let res = list_gateways::<CliSocks5Client, _>(args).await?;
println!("{}", output.format(&res));
Ok(())
}
+122 -48
View File
@@ -5,30 +5,48 @@ use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::config::{BaseClientConfig, Config, SocksClientPaths};
use crate::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::config::{BaseClientConfig, Config};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
use nym_client_core::config::{GatewayEndpointConfig, GroupBy, TopologyStructure};
use nym_client_core::error::ClientCoreError;
use nym_client_core::config::{GroupBy, TopologyStructure};
use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
use std::error::Error;
use std::net::IpAddr;
use std::sync::OnceLock;
mod add_gateway;
pub(crate) mod build_info;
mod import_credential;
pub mod init;
mod list_gateways;
pub(crate) mod run;
mod switch_gateway;
pub(crate) struct CliSocks5Client;
impl CliClient for CliSocks5Client {
const NAME: &'static str = "socks5";
type Error = Socks5ClientError;
type Config = Config;
async fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> {
try_upgrade_config(id).await
}
async fn try_load_current_config(id: &str) -> Result<Self::Config, Self::Error> {
try_load_current_config(id).await
}
}
fn pretty_build_info_static() -> &'static str {
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
@@ -59,7 +77,16 @@ pub(crate) enum Commands {
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(import_credential::Args),
ImportCredential(CommonClientImportCredentialArgs),
/// List all registered with gateways
ListGateways(list_gateways::Args),
/// Add new gateway to this client
AddGateway(add_gateway::Args),
/// Change the currently active gateway. Note that you must have already registered with the new gateway!
SwitchGateway(switch_gateway::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -93,6 +120,9 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
@@ -168,28 +198,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
}
fn persist_gateway_details(
storage_paths: &SocksClientPaths,
details: GatewayEndpointConfig,
) -> Result<(), Socks5ClientError> {
let details_store = OnDiskGatewayDetails::new(&storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?;
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
})
}
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.13 (which is incompatible with the next step, i.e. 1.1.19)
@@ -204,14 +213,23 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
let updated_step1: ConfigV1_1_20 = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated_step3, gateway_config) = updated_step2.upgrade()?;
persist_gateway_details(&updated_step3.storage_paths, gateway_config)?;
let old_paths = updated_step3.storage_paths.clone();
let updated_step4: ConfigV1_1_33 = updated_step3.into();
let updated = updated_step4.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated: Config = updated_step3.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
async fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
@@ -225,14 +243,23 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated_step2, gateway_config) = updated_step1.upgrade()?;
persist_gateway_details(&updated_step2.storage_paths, gateway_config)?;
let old_paths = updated_step2.storage_paths.clone();
let updated_step3: ConfigV1_1_33 = updated_step2.into();
let updated = updated_step3.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated: Config = updated_step2.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
async fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
@@ -243,14 +270,23 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
info!("It is going to get updated to the current specification.");
let (updated_step1, gateway_config) = old_config.upgrade()?;
persist_gateway_details(&updated_step1.storage_paths, gateway_config)?;
let old_paths = updated_step1.storage_paths.clone();
let updated_step2: ConfigV1_1_33 = updated_step1.into();
let updated = updated_step2.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated: Config = updated_step1.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError> {
async fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.30 (which is incompatible with the current one, i.e. +1.1.31)
let Ok(old_config) = ConfigV1_1_30::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
@@ -260,29 +296,68 @@ fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError> {
info!("It seems the client is using <= v1.1.30 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
let old_paths = old_config.storage_paths.clone();
let updated_step1: ConfigV1_1_33 = old_config.into();
let updated = updated_step1.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
None,
)
.await?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
if try_upgrade_v1_1_13_config(id)? {
async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.33 (which is incompatible with the current one, i.e. +1.1.34)
let Ok(old_config) = ConfigV1_1_33::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.33 config template.");
info!("It is going to get updated to the current specification.");
let old_paths = old_config.storage_paths.clone();
let updated = old_config.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated.storage_paths.common_paths,
None,
)
.await?;
updated.save_to_default_location()?;
Ok(true)
}
async fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
if try_upgrade_v1_1_13_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_20_config(id)? {
if try_upgrade_v1_1_20_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
if try_upgrade_v1_1_20_2_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_30_config(id)? {
if try_upgrade_v1_1_30_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_33_config(id).await? {
return Ok(());
}
Ok(())
}
fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
async fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
// try to load the config as is
if let Ok(cfg) = Config::read_from_default_path(id) {
return if !cfg.validate() {
@@ -293,7 +368,7 @@ fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
}
// we couldn't load it - try upgrading it from older revisions
try_upgrade_config(id)?;
try_upgrade_config(id).await?;
let config = match Config::read_from_default_path(id) {
Ok(cfg) => cfg,
@@ -313,7 +388,6 @@ fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_cli() {
+1 -1
View File
@@ -105,7 +105,7 @@ fn version_check(cfg: &Config) -> bool {
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
eprintln!("Starting client {}...", args.common_args.id);
let mut config = try_load_current_config(&args.common_args.id)?;
let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
@@ -0,0 +1,24 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_switch_gateway::{
switch_gateway, CommonClientSwitchGatewaysArgs,
};
#[derive(clap::Args, Clone, Debug)]
pub struct Args {
#[command(flatten)]
common_args: CommonClientSwitchGatewaysArgs,
}
impl AsRef<CommonClientSwitchGatewaysArgs> for Args {
fn as_ref(&self) -> &CommonClientSwitchGatewaysArgs {
&self.common_args
}
}
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
switch_gateway::<CliSocks5Client, _>(args).await
}
+3 -2
View File
@@ -3,7 +3,7 @@
use crate::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::cli_helpers::client_init::ClientConfig;
use nym_client_core::cli_helpers::CliClientConfig;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_config::{
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
@@ -24,6 +24,7 @@ pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
pub mod old_config_v1_1_33;
mod persistence;
mod template;
@@ -71,7 +72,7 @@ impl NymConfigTemplate for Config {
}
}
impl ClientConfig for Config {
impl CliClientConfig for Config {
fn common_paths(&self) -> &CommonClientPaths {
&self.storage_paths.common_paths
}
@@ -5,8 +5,8 @@ use crate::config::old_config_v1_1_20_2::{
ConfigV1_1_20_2, CoreConfigV1_1_20_2, SocksClientPathsV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::disk_persistence::old_v1_1_33::ClientKeysPathsV1_1_33;
use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::old_config_v1_1_20_2::ClientV1_1_20_2;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
@@ -50,7 +50,7 @@ impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
},
storage_paths: SocksClientPathsV1_1_20_2 {
common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths {
keys: ClientKeysPathsV1_1_33 {
private_identity_key_file: value.base.client.private_identity_key_file,
public_identity_key_file: value.base.client.public_identity_key_file,
private_encryption_key_file: value.base.client.private_encryption_key_file,
@@ -2,13 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::{
config::{default_config_filepath, persistence::SocksClientPaths},
error::Socks5ClientError,
};
use crate::config::old_config_v1_1_33::SocksClientPathsV1_1_33;
use crate::{config::default_config_filepath, error::Socks5ClientError};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::config::old_config_v1_1_33::OldGatewayEndpointConfigV1_1_33;
use nym_config::read_config_from_toml_file;
use serde::{Deserialize, Serialize};
use std::io;
@@ -43,11 +41,13 @@ impl ConfigV1_1_20_2 {
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> Result<(ConfigV1_1_30, GatewayEndpointConfig), Socks5ClientError> {
pub fn upgrade(
self,
) -> Result<(ConfigV1_1_30, OldGatewayEndpointConfigV1_1_33), Socks5ClientError> {
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
let config = ConfigV1_1_30 {
core: self.core.into(),
storage_paths: SocksClientPaths {
storage_paths: SocksClientPathsV1_1_33 {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
@@ -1,8 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::{default_config_filepath, Config};
use crate::config::default_config_filepath;
use crate::config::old_config_v1_1_33::{ConfigV1_1_33, SocksClientPathsV1_1_33};
use nym_bin_common::logging::LoggingSettings;
use nym_config::read_config_from_toml_file;
use nym_socks5_client_core::config::old_config_v1_1_30::ConfigV1_1_30 as CoreConfigV1_1_30;
@@ -15,17 +15,14 @@ use std::path::Path;
pub struct ConfigV1_1_30 {
pub core: CoreConfigV1_1_30,
// I'm leaving a landmine here for when the paths actually do change the next time,
// but propagating the change right now (in ALL clients) would be such a hassle...,
// so sorry for the next person looking at it : )
pub storage_paths: SocksClientPaths,
pub storage_paths: SocksClientPathsV1_1_33,
pub logging: LoggingSettings,
}
impl From<ConfigV1_1_30> for Config {
impl From<ConfigV1_1_30> for ConfigV1_1_33 {
fn from(value: ConfigV1_1_30) -> Self {
Config {
ConfigV1_1_33 {
core: value.core.into(),
storage_paths: value.storage_paths,
logging: LoggingSettings::default(),
@@ -0,0 +1,49 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{default_config_filepath, Config, SocksClientPaths};
use crate::error::Socks5ClientError;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_33::CommonClientPathsV1_1_33;
use nym_config::read_config_from_toml_file;
use nym_socks5_client_core::config::old_config_v1_1_33::ConfigV1_1_33 as CoreConfigV1_1_33;
use serde::{Deserialize, Serialize};
use std::io;
use std::path::Path;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct SocksClientPathsV1_1_33 {
#[serde(flatten)]
pub common_paths: CommonClientPathsV1_1_33,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_33 {
pub core: CoreConfigV1_1_33,
// \/ CHANGED
pub storage_paths: SocksClientPathsV1_1_33,
// /\ CHANGED
pub logging: LoggingSettings,
}
impl ConfigV1_1_33 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, Socks5ClientError> {
Ok(Config {
core: self.core.into(),
storage_paths: SocksClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
})
}
}
+4 -8
View File
@@ -50,10 +50,6 @@ keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key
# Path to file containing public encryption key.
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
@@ -64,9 +60,9 @@ credentials_database = '{{ storage_paths.credentials_database }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# Path to the file containing information about gateway used by this client,
# i.e. details such as its public key, owner address or the network information.
gateway_details = '{{ storage_paths.gateway_details }}'
# Path to the file containing information about gateways used by this client,
# i.e. details such as their public keys, owner addresses or the network information.
gateway_registrations = '{{ storage_paths.gateway_registrations }}'
##### socket config options #####
@@ -77,7 +73,7 @@ provider_mix_address = '{{ core.socks5.provider_mix_address }}'
# The address on which the client will be listening for incoming requests
# (default: 127.0.0.1:1080)
bind_adddress = '{{ core.socks5.bind_adddress }}'
bind_address = '{{ core.socks5.bind_address }}'
# Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
# While this is going to hide its actual address information, it will make the actual communication
+6 -18
View File
@@ -1,6 +1,6 @@
use nym_client_core::error::ClientCoreError;
use nym_credential_storage::error::StorageError;
use time::OffsetDateTime;
use nym_id::NymIdError;
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
@@ -23,21 +23,9 @@ pub enum Socks5ClientError {
#[error(transparent)]
ClientCoreError(#[from] ClientCoreError),
#[error("failed to store credential: {source}")]
CredentialStorageFailure {
#[from]
source: StorageError,
},
#[error(transparent)]
ConfigUpgradeFailure(#[from] nym_client_core::config::ConfigUpgradeFailure),
#[error(
"failed to deserialize provided credential using revision {storage_revision}: {source}"
)]
CredentialDeserializationFailure {
storage_revision: u8,
#[source]
source: nym_credentials::error::Error,
},
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
+4 -4
View File
@@ -1,13 +1,13 @@
[package]
name = "async-file-watcher"
name = "nym-async-file-watcher"
version = "0.1.0"
edition = "2021"
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4"
tokio = { workspace = true, features = ["time"] }
futures = { workspace = true }
log = "0.4"
notify = "5.1.0"
tokio = { workspace = true, features = ["time"] }
+11 -6
View File
@@ -18,6 +18,7 @@ pub mod acquire;
pub mod error;
mod utils;
#[derive(Debug)]
pub struct BandwidthController<C, St> {
storage: St,
client: C,
@@ -49,6 +50,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
/// It marks any retrieved intermediate credentials as expired.
pub async fn get_next_usable_credential(
&self,
gateway_id: &str,
) -> Result<RetrievedCredential, BandwidthControllerError>
where
<St as Storage>::StorageError: Send + Sync + 'static,
@@ -56,7 +58,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
loop {
let Some(maybe_next) = self
.storage
.get_next_unspent_credential()
.get_next_unspent_credential(gateway_id)
.await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?
else {
@@ -114,12 +116,13 @@ impl<C, St: Storage> BandwidthController<C, St> {
pub async fn prepare_bandwidth_credential(
&self,
gateway_id: &str,
) -> Result<PreparedCredential, BandwidthControllerError>
where
C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static,
{
let retrieved_credential = self.get_next_usable_credential().await?;
let retrieved_credential = self.get_next_usable_credential(gateway_id).await?;
let epoch_id = retrieved_credential.credential.epoch_id();
let credential_id = retrieved_credential.credential_id;
@@ -137,14 +140,16 @@ impl<C, St: Storage> BandwidthController<C, St> {
})
}
pub async fn consume_credential(&self, id: i64) -> Result<(), BandwidthControllerError>
pub async fn consume_credential(
&self,
id: i64,
gateway_id: &str,
) -> Result<(), BandwidthControllerError>
where
<St as Storage>::StorageError: Send + Sync + 'static,
{
// JS: shouldn't we send some contract/validator/gateway message here to actually, you know,
// consume it?
self.storage
.consume_coconut_credential(id)
.consume_coconut_credential(id, gateway_id)
.await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))
}
-1
View File
@@ -8,7 +8,6 @@ license = { workspace = true }
repository = { workspace = true }
[dependencies]
atty = "0.2"
const-str = "0.5.6"
clap = { workspace = true, features = ["derive"] }
clap_complete = "4.0"
@@ -69,6 +69,35 @@ impl BinaryBuildInformation {
}
}
// Varient where we want to use the metadata generated by vergen in the consuming crate.
pub const fn new_with_local_vergen(
binary_name: &'static str,
build_timestamp: &'static str,
build_version: &'static str,
commit_sha: &'static str,
commit_timestamp: &'static str,
commit_branch: &'static str,
) -> Self {
let cargo_debug = env!("VERGEN_CARGO_DEBUG");
let cargo_profile = if const_str::equal!(cargo_debug, "true") {
"debug"
} else {
"release"
};
BinaryBuildInformation {
binary_name,
build_timestamp,
build_version,
commit_sha,
commit_timestamp,
commit_branch,
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile,
}
}
pub fn to_owned(&self) -> BinaryBuildInformationOwned {
BinaryBuildInformationOwned {
binary_name: self.binary_name.to_owned(),
@@ -187,3 +216,33 @@ macro_rules! bin_info_owned {
.to_owned()
};
}
// variant that picks up the vergen build information generated by the build.rs in the consumer
// crate.
#[macro_export]
macro_rules! bin_info_local_vergen {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
#[macro_export]
macro_rules! bin_info_local_vergen_owned {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
+3 -2
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::io::IsTerminal;
#[cfg(feature = "tracing")]
pub use opentelemetry;
@@ -14,7 +15,7 @@ pub use tracing_subscriber;
#[cfg(feature = "tracing")]
pub use tracing_tree;
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[derive(Debug, Default, Copy, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettings {
// well, we need to implement something here at some point...
@@ -120,7 +121,7 @@ pub fn banner(crate_name: &str, crate_version: &str) -> String {
}
pub fn maybe_print_banner(crate_name: &str, crate_version: &str) {
if atty::is(atty::Stream::Stdout) {
if std::io::stdout().is_terminal() {
println!("{}", banner(crate_name, crate_version))
}
}
+28 -18
View File
@@ -11,33 +11,35 @@ license.workspace = true
[dependencies]
async-trait = { workspace = true }
base64 = "0.21.2"
bs58 = { workspace = true }
cfg-if = "1.0.0"
clap = { workspace = true, optional = true }
dashmap = { workspace = true }
dirs = "4.0"
futures = { workspace = true }
humantime-serde = "1.0"
humantime-serde = { workspace = true }
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = "0.10.6"
si-scale = "0.2.2"
tap = "1.0.1"
thiserror = { workspace = true }
url = { workspace = true, features = ["serde"] }
tungstenite = { workspace = true, default-features = false }
tokio = { workspace = true, features = ["macros"]}
time = "0.3.17"
tokio = { workspace = true, features = ["macros"] }
time = { workspace = true }
zeroize = { workspace = true }
# internal
nym-id = { path = "../nym-id" }
nym-bandwidth-controller = { path = "../bandwidth-controller" }
nym-config = { path = "../config" }
nym-country-group = { path = "../country-group" }
nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-metrics = { path = "../nym-metrics" }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" }
nym-pemstore = { path = "../pemstore" }
@@ -46,7 +48,22 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" }
si-scale = "0.2.2"
nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"]}
nym-client-core-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" }
### For serving prometheus metrics
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper]
version = "1"
features = ["server", "http1"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.http-body-util]
version = "0.1"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper-util]
version = "0.1"
features = ["tokio"]
###
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
@@ -58,11 +75,7 @@ features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.20.1"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
features = ["rustls-tls-native-roots"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
workspace = true
@@ -89,13 +102,10 @@ features = ["wasm-bindgen"]
[dev-dependencies]
tempfile = "3.1.0"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
default = []
cli = ["clap"]
fs-surb-storage = ["sqlx"]
fs-surb-storage = ["nym-client-core-surb-storage/fs-surb-storage"]
fs-gateways-storage = ["nym-client-core-gateways-storage/fs-gateways-storage"]
wasm = ["nym-gateway-client/wasm"]
metrics-server = []
@@ -0,0 +1,26 @@
[package]
name = "nym-client-core-config-types"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
humantime-serde = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror.workspace = true
url = { workspace = true, features = ["serde"] }
nym-config = { path = "../../config" }
nym-country-group = { path = "../../country-group" }
nym-pemstore = { path = "../../pemstore", optional = true }
# those are pulling so many deps T.T
nym-sphinx-params = { path = "../../nymsphinx/params" }
nym-sphinx-addressing = { path = "../../nymsphinx/addressing" }
[features]
disk-persistence = ["nym-pemstore"]
@@ -1,16 +1,53 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
pub mod old;
// preserve old structure for easier migration
pub use old::{old_v1_1_20_2, old_v1_1_33};
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
pub const DEFAULT_GATEWAYS_DETAILS_DB_FILENAME: &str = "gateways_registrations.sqlite";
pub const DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME: &str = "private_identity.pem";
pub const DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME: &str = "public_identity.pem";
pub const DEFAULT_PRIVATE_ENCRYPTION_KEY_FILENAME: &str = "private_encryption.pem";
pub const DEFAULT_PUBLIC_ENCRYPTION_KEY_FILENAME: &str = "public_encryption.pem";
pub const DEFAULT_GATEWAY_SHARED_KEY_FILENAME: &str = "gateway_shared.pem";
pub const DEFAULT_ACK_KEY_FILENAME: &str = "ack_key.pem";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPaths {
pub keys: ClientKeysPaths,
/// Path to the file containing information about gateways used by this client,
/// i.e. details such as their public keys, owner addresses or the network information.
pub gateway_registrations: PathBuf,
/// Path to the database containing bandwidth credentials of this client.
pub credentials_database: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
pub reply_surb_database: PathBuf,
}
impl CommonClientPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
CommonClientPaths {
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
gateway_registrations: base_dir.join(DEFAULT_GATEWAYS_DETAILS_DB_FILENAME),
keys: ClientKeysPaths::new_base(base_data_directory),
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientKeysPaths {
/// Path to file containing private identity key.
@@ -25,10 +62,6 @@ pub struct ClientKeysPaths {
/// Path to file containing public encryption key.
pub public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
pub gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
pub ack_key_file: PathBuf,
@@ -43,7 +76,6 @@ impl ClientKeysPaths {
public_identity_key_file: base_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
private_encryption_key_file: base_dir.join(DEFAULT_PRIVATE_ENCRYPTION_KEY_FILENAME),
public_encryption_key_file: base_dir.join(DEFAULT_PUBLIC_ENCRYPTION_KEY_FILENAME),
gateway_shared_key_file: base_dir.join(DEFAULT_GATEWAY_SHARED_KEY_FILENAME),
ack_key_file: base_dir.join(DEFAULT_ACK_KEY_FILENAME),
}
}
@@ -67,7 +99,6 @@ impl ClientKeysPaths {
|| matches!(self.private_identity_key_file.try_exists(), Ok(true))
|| matches!(self.public_encryption_key_file.try_exists(), Ok(true))
|| matches!(self.private_encryption_key_file.try_exists(), Ok(true))
|| matches!(self.gateway_shared_key_file.try_exists(), Ok(true))
|| matches!(self.ack_key_file.try_exists(), Ok(true))
}
@@ -76,14 +107,9 @@ impl ClientKeysPaths {
.or_else(|| file_exists(&self.private_identity_key_file))
.or_else(|| file_exists(&self.public_encryption_key_file))
.or_else(|| file_exists(&self.private_encryption_key_file))
.or_else(|| file_exists(&self.gateway_shared_key_file))
.or_else(|| file_exists(&self.ack_key_file))
}
pub fn gateway_key_file_exists(&self) -> bool {
matches!(self.gateway_shared_key_file.try_exists(), Ok(true))
}
pub fn private_identity_key(&self) -> &Path {
&self.private_identity_key_file
}
@@ -100,10 +126,6 @@ impl ClientKeysPaths {
&self.public_encryption_key_file
}
pub fn gateway_shared_key(&self) -> &Path {
&self.gateway_shared_key_file
}
pub fn ack_key(&self) -> &Path {
&self.ack_key_file
}
@@ -0,0 +1,9 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod v1;
pub mod v2;
// aliases for backwards compatibility
pub use v1 as old_v1_1_20_2;
pub use v2 as old_v1_1_33;
@@ -0,0 +1,37 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::disk_persistence::old::v2::{
ClientKeysPathsV2, CommonClientPathsV2, DEFAULT_GATEWAY_DETAILS_FILENAME,
};
use crate::error::ConfigUpgradeFailure;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
// aliases for backwards compatibility
pub type CommonClientPathsV1_1_20_2 = CommonClientPathsV1;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPathsV1 {
pub keys: ClientKeysPathsV2,
pub credentials_database: PathBuf,
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV1 {
pub fn upgrade_default(self) -> Result<CommonClientPathsV2, ConfigUpgradeFailure> {
let data_dir = self
.reply_surb_database
.parent()
.ok_or_else(|| ConfigUpgradeFailure {
current_version: "1.1.20-2".to_string(),
})?;
Ok(CommonClientPathsV2 {
keys: self.keys,
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
credentials_database: self.credentials_database,
reply_surb_database: self.reply_surb_database,
})
}
}
@@ -0,0 +1,85 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::disk_persistence::ClientKeysPaths;
use crate::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAYS_DETAILS_DB_FILENAME};
use crate::error::ConfigUpgradeFailure;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
pub const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
// aliases for backwards compatibility
pub type CommonClientPathsV1_1_33 = CommonClientPathsV2;
pub type ClientKeysPathsV1_1_33 = ClientKeysPathsV2;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientKeysPathsV2 {
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
pub public_identity_key_file: PathBuf,
/// Path to file containing private encryption key.
pub private_encryption_key_file: PathBuf,
/// Path to file containing public encryption key.
pub public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
pub gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
pub ack_key_file: PathBuf,
}
impl ClientKeysPathsV2 {
pub fn upgrade(self) -> ClientKeysPaths {
ClientKeysPaths {
private_identity_key_file: self.private_identity_key_file,
public_identity_key_file: self.public_identity_key_file,
private_encryption_key_file: self.private_encryption_key_file,
public_encryption_key_file: self.public_encryption_key_file,
ack_key_file: self.ack_key_file,
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPathsV2 {
pub keys: ClientKeysPathsV2,
/// Path to the file containing information about gateway used by this client,
/// i.e. details such as its public key, owner address or the network information.
pub gateway_details: PathBuf,
/// Path to the database containing bandwidth credentials of this client.
pub credentials_database: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV2 {
// note that during the upgrade process, the caller will need to extract the key and gateway details
// manually and resave them in the new database
pub fn upgrade_default(self) -> Result<CommonClientPaths, ConfigUpgradeFailure> {
let data_dir = self
.gateway_details
.parent()
.ok_or_else(|| ConfigUpgradeFailure {
current_version: "1.1.33".to_string(),
})?;
Ok(CommonClientPaths {
keys: self.keys.upgrade(),
gateway_registrations: data_dir.join(DEFAULT_GATEWAYS_DETAILS_DB_FILENAME),
credentials_database: self.credentials_database,
reply_surb_database: self.reply_surb_database,
})
}
}
@@ -0,0 +1,10 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
#[derive(Debug, Error)]
#[error("unable to upgrade config file from `{current_version}`")]
pub struct ConfigUpgradeFailure {
pub current_version: String,
}
+602
View File
@@ -0,0 +1,602 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_config::defaults::NymNetworkDetails;
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
#[cfg(feature = "disk-persistence")]
pub mod disk_persistence;
pub mod error;
pub mod old;
pub use error::ConfigUpgradeFailure;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
pub use nym_country_group::CountryGroup;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub client: Client,
#[serde(default)]
pub debug: DebugConfig,
}
impl Config {
pub fn new<S1, S2>(id: S1, version: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
Config {
client: Client::new_default(id, version),
debug: Default::default(),
}
}
pub fn from_client_config(client: Client, debug: DebugConfig) -> Self {
Config { client, debug }
}
pub fn validate(&self) -> bool {
self.debug.validate()
}
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
self.debug = debug;
self
}
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
self.client.disabled_credentials_mode = disabled_credentials_mode;
self
}
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
self.client.nyxd_urls = urls;
self
}
pub fn set_custom_nyxd(&mut self, nyxd_urls: Vec<Url>) {
self.client.nyxd_urls = nyxd_urls;
}
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
self.client.nym_api_urls = nym_api_urls;
self
}
pub fn set_custom_nym_apis(&mut self, nym_api_urls: Vec<Url>) {
self.client.nym_api_urls = nym_api_urls;
}
pub fn with_high_default_traffic_volume(mut self, enabled: bool) -> Self {
if enabled {
self.set_high_default_traffic_volume();
}
self
}
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
self.debug.traffic.packet_type = packet_type;
self
}
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
// basically don't really send cover messages
self.debug.cover_traffic.loop_cover_traffic_average_delay =
Duration::from_millis(2_000_000);
// 250 "real" messages / s
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
}
pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_poisson_process()
}
self
}
pub fn set_no_poisson_process(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic()
}
self
}
pub fn set_no_cover_traffic(&mut self) {
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic_with_keepalive(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic_with_keepalive()
}
self
}
pub fn set_no_cover_traffic_with_keepalive(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
self.debug.cover_traffic.loop_cover_traffic_average_delay = Duration::from_secs(5);
}
pub fn with_disabled_topology_refresh(mut self, disable_topology_refresh: bool) -> Self {
self.debug.topology.disable_refreshing = disable_topology_refresh;
self
}
pub fn with_topology_structure(mut self, topology_structure: TopologyStructure) -> Self {
self.set_topology_structure(topology_structure);
self
}
pub fn set_topology_structure(&mut self, topology_structure: TopologyStructure) {
self.debug.topology.topology_structure = topology_structure;
}
pub fn with_no_per_hop_delays(mut self, no_per_hop_delays: bool) -> Self {
if no_per_hop_delays {
self.set_no_per_hop_delays()
}
self
}
pub fn set_no_per_hop_delays(&mut self) {
self.debug.traffic.average_packet_delay = Duration::ZERO;
self.debug.acknowledgements.average_ack_delay = Duration::ZERO;
}
pub fn with_secondary_packet_size(mut self, secondary_packet_size: Option<PacketSize>) -> Self {
self.set_secondary_packet_size(secondary_packet_size);
self
}
pub fn set_secondary_packet_size(&mut self, secondary_packet_size: Option<PacketSize>) {
self.debug.traffic.secondary_packet_size = secondary_packet_size;
}
pub fn set_custom_version(&mut self, version: &str) {
self.client.version = version.to_string();
}
pub fn get_id(&self) -> String {
self.client.id.clone()
}
pub fn get_disabled_credentials_mode(&self) -> bool {
self.client.disabled_credentials_mode
}
pub fn get_validator_endpoints(&self) -> Vec<Url> {
self.client.nyxd_urls.clone()
}
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone()
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct Client {
/// Version of the client for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
}
impl Client {
pub fn new_default<S1, S2>(id: S1, version: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
let network = NymNetworkDetails::new_mainnet();
let nyxd_urls = network
.endpoints
.iter()
.map(|validator| validator.nyxd_url())
.collect();
let nym_api_urls = network
.endpoints
.iter()
.filter_map(|validator| validator.api_url())
.collect::<Vec<_>>();
Client {
version: version.into(),
id: id.into(),
disabled_credentials_mode: true,
nyxd_urls,
nym_api_urls,
}
}
pub fn new<S: Into<String>>(
id: S,
version: S,
disabled_credentials_mode: bool,
nyxd_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
) -> Self {
Client {
version: version.into(),
id: id.into(),
disabled_credentials_mode,
nyxd_urls,
nym_api_urls,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Traffic {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity.
/// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
}
impl Traffic {
pub fn validate(&self) -> bool {
if let Some(secondary_packet_size) = self.secondary_packet_size {
if secondary_packet_size == PacketSize::AckPacket
|| secondary_packet_size == self.primary_packet_size
{
return false;
}
}
true
}
}
impl Default for Traffic {
fn default() -> Self {
Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
/// Only applicable if `secondary_packet_size` is enabled.
pub cover_traffic_primary_size_ratio: f64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTraffic {
fn default() -> Self {
CoverTraffic {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnection {
fn default() -> Self {
GatewayConnection {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for Acknowledgements {
fn default() -> Self {
Acknowledgements {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructure,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructure {
#[default]
NymApi,
GeoAware(GroupBy),
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupBy {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl std::fmt::Display for GroupBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupBy::CountryGroup(group) => write!(f, "group: {group}"),
GroupBy::NymAddress(address) => write!(f, "address: {address}"),
}
}
}
impl Default for Topology {
fn default() -> Self {
Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructure::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
pub maximum_reply_surb_storage_threshold: usize,
/// Defines the minimum number of reply surbs the client would request.
pub minimum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs the client would request.
pub maximum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
pub maximum_allowed_reply_surb_request_size: u32,
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
/// for more even though in theory they wouldn't need to.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
/// Defines maximum amount of time the client is going to wait for reply surbs before
/// deciding it's never going to get them and would drop all pending messages
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
/// Defines maximum amount of time given reply surb is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
/// Defines maximum amount of time given reply key is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
/// the default value is used.
pub surb_mix_hops: Option<u8>,
}
impl Default for ReplySurbs {
fn default() -> Self {
ReplySurbs {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
surb_mix_hops: None,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfig {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
}
impl DebugConfig {
pub fn validate(&self) -> bool {
// no other sections have explicit requirements (yet)
self.traffic.validate()
}
}
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
// something manually at some point
#[allow(clippy::derivable_impls)]
impl Default for DebugConfig {
fn default() -> Self {
DebugConfig {
traffic: Default::default(),
cover_traffic: Default::default(),
gateway_connection: Default::default(),
acknowledgements: Default::default(),
topology: Default::default(),
reply_surbs: Default::default(),
}
}
}
@@ -0,0 +1,15 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod v1;
pub mod v2;
pub mod v3;
pub mod v4;
pub mod v5;
// aliases for backwards compatibility
pub use v1 as old_config_v1_1_13;
pub use v2 as old_config_v1_1_20;
pub use v3 as old_config_v1_1_20_2;
pub use v4 as old_config_v1_1_30;
pub use v5 as old_config_v1_1_33;
@@ -1,23 +1,28 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_20::{
AcknowledgementsV1_1_20, ClientV1_1_20, ConfigV1_1_20, CoverTrafficV1_1_20, DebugConfigV1_1_20,
GatewayConnectionV1_1_20, LoggingV1_1_20, ReplySurbsV1_1_20, TopologyV1_1_20, TrafficV1_1_20,
DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY,
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_AGE, DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
use crate::old::v2::{
AcknowledgementsV2, ClientV2, ConfigV2, CoverTrafficV2, DebugConfigV2, GatewayConnectionV2,
LoggingV2, ReplySurbsV2, TopologyV2, TrafficV2, DEFAULT_ACK_WAIT_ADDITION,
DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
};
use nym_sphinx::params::PacketSize;
use nym_sphinx_params::PacketSize;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::time::Duration;
// aliases for backwards compatibility
pub type OldConfigV1_1_13<T> = ConfigV1<T>;
pub type OldLoggingV1_1_13 = LoggingV1;
pub type OldDebugConfigV1_1_13 = DebugConfigV1;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
@@ -38,28 +43,28 @@ impl From<ExtendedPacketSize> for PacketSize {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13<T> {
pub client: ClientV1_1_20<T>,
pub struct ConfigV1<T> {
pub client: ClientV2<T>,
#[serde(default)]
pub logging: OldLoggingV1_1_13,
pub logging: LoggingV1,
#[serde(default)]
pub debug: OldDebugConfigV1_1_13,
pub debug: DebugConfigV1,
}
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldLoggingV1_1_13 {}
pub struct LoggingV1 {}
impl From<OldLoggingV1_1_13> for LoggingV1_1_20 {
fn from(_value: OldLoggingV1_1_13) -> Self {
LoggingV1_1_20 {}
impl From<LoggingV1> for LoggingV2 {
fn from(_value: LoggingV1) -> Self {
LoggingV2 {}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct OldDebugConfigV1_1_13 {
pub struct DebugConfigV1 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
@@ -114,10 +119,10 @@ pub struct OldDebugConfigV1_1_13 {
pub maximum_reply_key_age: Duration,
}
impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 {
fn from(value: OldDebugConfigV1_1_13) -> Self {
DebugConfigV1_1_20 {
traffic: TrafficV1_1_20 {
impl From<DebugConfigV1> for DebugConfigV2 {
fn from(value: DebugConfigV1) -> Self {
DebugConfigV2 {
traffic: TrafficV2 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -125,25 +130,25 @@ impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 {
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: value.use_extended_packet_size.map(Into::into),
},
cover_traffic: CoverTrafficV1_1_20 {
cover_traffic: CoverTrafficV2 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
..CoverTrafficV1_1_20::default()
..CoverTrafficV2::default()
},
gateway_connection: GatewayConnectionV1_1_20 {
gateway_connection: GatewayConnectionV2 {
gateway_response_timeout: value.gateway_response_timeout,
},
acknowledgements: AcknowledgementsV1_1_20 {
acknowledgements: AcknowledgementsV2 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
},
topology: TopologyV1_1_20 {
topology: TopologyV2 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: false,
},
reply_surbs: ReplySurbsV1_1_20 {
reply_surbs: ReplySurbsV2 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -161,9 +166,9 @@ impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 {
}
}
impl Default for OldDebugConfigV1_1_13 {
impl Default for DebugConfigV1 {
fn default() -> Self {
OldDebugConfigV1_1_13 {
DebugConfigV1 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
@@ -190,10 +195,10 @@ impl Default for OldDebugConfigV1_1_13 {
}
}
impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_20<U> {
fn from(value: OldConfigV1_1_13<T>) -> Self {
ConfigV1_1_20 {
client: ClientV1_1_20 {
impl<T, U> From<ConfigV1<T>> for ConfigV2<U> {
fn from(value: ConfigV1<T>) -> Self {
ConfigV2 {
client: ClientV2 {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
@@ -1,12 +1,11 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_20_2::{
AcknowledgementsV1_1_20_2, CoverTrafficV1_1_20_2, DebugConfigV1_1_20_2,
GatewayConnectionV1_1_20_2, GatewayEndpointConfigV1_1_20_2, ReplySurbsV1_1_20_2,
TopologyV1_1_20_2, TrafficV1_1_20_2,
use crate::old::v3::{
AcknowledgementsV3, CoverTrafficV3, DebugConfigV3, GatewayConnectionV3,
GatewayEndpointConfigV3, ReplySurbsV3, TopologyV3, TrafficV3,
};
use nym_sphinx::params::{PacketSize, PacketType};
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
@@ -54,27 +53,41 @@ pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(
// 24 hours
pub(crate) const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_20<T> = ConfigV2<T>;
pub type ClientV1_1_20<T> = ClientV2<T>;
pub type LoggingV1_1_20 = LoggingV2;
pub type DebugConfigV1_1_20 = DebugConfigV2;
pub type GatewayEndpointConfigV1_1_20 = GatewayEndpointConfigV2;
pub type TrafficV1_1_20 = TrafficV2;
pub type CoverTrafficV1_1_20 = CoverTrafficV2;
pub type GatewayConnectionV1_1_20 = GatewayConnectionV2;
pub type AcknowledgementsV1_1_20 = AcknowledgementsV2;
pub type TopologyV1_1_20 = TopologyV2;
pub type ReplySurbsV1_1_20 = ReplySurbsV2;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20<T> {
pub client: ClientV1_1_20<T>,
pub struct ConfigV2<T> {
pub client: ClientV2<T>,
#[serde(default)]
pub logging: LoggingV1_1_20,
pub logging: LoggingV2,
#[serde(default)]
pub debug: DebugConfigV1_1_20,
pub debug: DebugConfigV2,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_20 {
pub struct GatewayEndpointConfigV2 {
pub gateway_id: String,
pub gateway_owner: String,
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
fn from(value: GatewayEndpointConfigV1_1_20) -> Self {
GatewayEndpointConfigV1_1_20_2 {
impl From<GatewayEndpointConfigV2> for GatewayEndpointConfigV3 {
fn from(value: GatewayEndpointConfigV2) -> Self {
GatewayEndpointConfigV3 {
gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
@@ -83,7 +96,7 @@ impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV1_1_20<T> {
pub struct ClientV2<T> {
pub version: String,
pub id: String,
#[serde(default)]
@@ -98,7 +111,7 @@ pub struct ClientV1_1_20<T> {
pub public_encryption_key_file: PathBuf,
pub gateway_shared_key_file: PathBuf,
pub ack_key_file: PathBuf,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20,
pub gateway_endpoint: GatewayEndpointConfigV2,
pub database_path: PathBuf,
#[serde(default)]
pub reply_surb_database_path: PathBuf,
@@ -110,11 +123,11 @@ pub struct ClientV1_1_20<T> {
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingV1_1_20 {}
pub struct LoggingV2 {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV1_1_20 {
pub struct TrafficV2 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
@@ -124,9 +137,9 @@ pub struct TrafficV1_1_20 {
pub secondary_packet_size: Option<PacketSize>,
}
impl From<TrafficV1_1_20> for TrafficV1_1_20_2 {
fn from(value: TrafficV1_1_20) -> Self {
TrafficV1_1_20_2 {
impl From<TrafficV2> for TrafficV3 {
fn from(value: TrafficV2) -> Self {
TrafficV3 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -138,9 +151,9 @@ impl From<TrafficV1_1_20> for TrafficV1_1_20_2 {
}
}
impl Default for TrafficV1_1_20 {
impl Default for TrafficV2 {
fn default() -> Self {
TrafficV1_1_20 {
TrafficV2 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -152,16 +165,16 @@ impl Default for TrafficV1_1_20 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV1_1_20 {
pub struct CoverTrafficV2 {
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
pub cover_traffic_primary_size_ratio: f64,
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTrafficV1_1_20> for CoverTrafficV1_1_20_2 {
fn from(value: CoverTrafficV1_1_20) -> Self {
CoverTrafficV1_1_20_2 {
impl From<CoverTrafficV2> for CoverTrafficV3 {
fn from(value: CoverTrafficV2) -> Self {
CoverTrafficV3 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
@@ -169,9 +182,9 @@ impl From<CoverTrafficV1_1_20> for CoverTrafficV1_1_20_2 {
}
}
impl Default for CoverTrafficV1_1_20 {
impl Default for CoverTrafficV2 {
fn default() -> Self {
CoverTrafficV1_1_20 {
CoverTrafficV2 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
@@ -181,22 +194,22 @@ impl Default for CoverTrafficV1_1_20 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_20 {
pub struct GatewayConnectionV2 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV1_1_20> for GatewayConnectionV1_1_20_2 {
fn from(value: GatewayConnectionV1_1_20) -> Self {
GatewayConnectionV1_1_20_2 {
impl From<GatewayConnectionV2> for GatewayConnectionV3 {
fn from(value: GatewayConnectionV2) -> Self {
GatewayConnectionV3 {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV1_1_20 {
impl Default for GatewayConnectionV2 {
fn default() -> Self {
GatewayConnectionV1_1_20 {
GatewayConnectionV2 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -204,7 +217,7 @@ impl Default for GatewayConnectionV1_1_20 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_20 {
pub struct AcknowledgementsV2 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
@@ -212,9 +225,9 @@ pub struct AcknowledgementsV1_1_20 {
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV1_1_20> for AcknowledgementsV1_1_20_2 {
fn from(value: AcknowledgementsV1_1_20) -> Self {
AcknowledgementsV1_1_20_2 {
impl From<AcknowledgementsV2> for AcknowledgementsV3 {
fn from(value: AcknowledgementsV2) -> Self {
AcknowledgementsV3 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
@@ -222,9 +235,9 @@ impl From<AcknowledgementsV1_1_20> for AcknowledgementsV1_1_20_2 {
}
}
impl Default for AcknowledgementsV1_1_20 {
impl Default for AcknowledgementsV2 {
fn default() -> Self {
AcknowledgementsV1_1_20 {
AcknowledgementsV2 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -234,7 +247,7 @@ impl Default for AcknowledgementsV1_1_20 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_20 {
pub struct TopologyV2 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
@@ -242,9 +255,9 @@ pub struct TopologyV1_1_20 {
pub disable_refreshing: bool,
}
impl From<TopologyV1_1_20> for TopologyV1_1_20_2 {
fn from(value: TopologyV1_1_20) -> Self {
TopologyV1_1_20_2 {
impl From<TopologyV2> for TopologyV3 {
fn from(value: TopologyV2) -> Self {
TopologyV3 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
@@ -252,9 +265,9 @@ impl From<TopologyV1_1_20> for TopologyV1_1_20_2 {
}
}
impl Default for TopologyV1_1_20 {
impl Default for TopologyV2 {
fn default() -> Self {
TopologyV1_1_20 {
TopologyV2 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
@@ -264,7 +277,7 @@ impl Default for TopologyV1_1_20 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV1_1_20 {
pub struct ReplySurbsV2 {
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
@@ -280,9 +293,9 @@ pub struct ReplySurbsV1_1_20 {
pub maximum_reply_key_age: Duration,
}
impl From<ReplySurbsV1_1_20> for ReplySurbsV1_1_20_2 {
fn from(value: ReplySurbsV1_1_20) -> Self {
ReplySurbsV1_1_20_2 {
impl From<ReplySurbsV2> for ReplySurbsV3 {
fn from(value: ReplySurbsV2) -> Self {
ReplySurbsV3 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -297,9 +310,9 @@ impl From<ReplySurbsV1_1_20> for ReplySurbsV1_1_20_2 {
}
}
impl Default for ReplySurbsV1_1_20 {
impl Default for ReplySurbsV2 {
fn default() -> Self {
ReplySurbsV1_1_20 {
ReplySurbsV2 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -316,18 +329,18 @@ impl Default for ReplySurbsV1_1_20 {
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1_1_20 {
pub traffic: TrafficV1_1_20,
pub cover_traffic: CoverTrafficV1_1_20,
pub gateway_connection: GatewayConnectionV1_1_20,
pub acknowledgements: AcknowledgementsV1_1_20,
pub topology: TopologyV1_1_20,
pub reply_surbs: ReplySurbsV1_1_20,
pub struct DebugConfigV2 {
pub traffic: TrafficV2,
pub cover_traffic: CoverTrafficV2,
pub gateway_connection: GatewayConnectionV2,
pub acknowledgements: AcknowledgementsV2,
pub topology: TopologyV2,
pub reply_surbs: ReplySurbsV2,
}
impl From<DebugConfigV1_1_20> for DebugConfigV1_1_20_2 {
fn from(value: DebugConfigV1_1_20) -> Self {
DebugConfigV1_1_20_2 {
impl From<DebugConfigV2> for DebugConfigV3 {
fn from(value: DebugConfigV2) -> Self {
DebugConfigV3 {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(),
@@ -1,12 +1,12 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_30::{
AcknowledgementsV1_1_30, ClientV1_1_30, ConfigV1_1_30, CoverTrafficV1_1_30, DebugConfigV1_1_30,
GatewayConnectionV1_1_30, ReplySurbsV1_1_30, TopologyV1_1_30, TrafficV1_1_30,
use crate::old::v4::{
AcknowledgementsV4, ClientV4, ConfigV4, CoverTrafficV4, DebugConfigV4, GatewayConnectionV4,
ReplySurbsV4, TopologyV4, TrafficV4,
};
use crate::config::GatewayEndpointConfig;
use nym_sphinx::params::{PacketSize, PacketType};
use crate::old::v5::GatewayEndpointConfigV5;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -50,18 +50,31 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_20_2 = ConfigV3;
pub type ClientV1_1_20_2 = ClientV3;
pub type DebugConfigV1_1_20_2 = DebugConfigV3;
pub type GatewayEndpointConfigV1_1_20_2 = GatewayEndpointConfigV3;
pub type TrafficV1_1_20_2 = TrafficV3;
pub type CoverTrafficV1_1_20_2 = CoverTrafficV3;
pub type GatewayConnectionV1_1_20_2 = GatewayConnectionV3;
pub type AcknowledgementsV1_1_20_2 = AcknowledgementsV3;
pub type TopologyV1_1_20_2 = TopologyV3;
pub type ReplySurbsV1_1_20_2 = ReplySurbsV3;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20_2 {
pub client: ClientV1_1_20_2,
pub struct ConfigV3 {
pub client: ClientV3,
#[serde(default)]
pub debug: DebugConfigV1_1_20_2,
pub debug: DebugConfigV3,
}
impl From<ConfigV1_1_20_2> for ConfigV1_1_30 {
fn from(value: ConfigV1_1_20_2) -> Self {
ConfigV1_1_30 {
impl From<ConfigV3> for ConfigV4 {
fn from(value: ConfigV3) -> Self {
ConfigV4 {
client: value.client.into(),
debug: value.debug.into(),
}
@@ -69,7 +82,7 @@ impl From<ConfigV1_1_20_2> for ConfigV1_1_30 {
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_20_2 {
pub struct GatewayEndpointConfigV3 {
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
@@ -81,9 +94,9 @@ pub struct GatewayEndpointConfigV1_1_20_2 {
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV1_1_20_2> for GatewayEndpointConfig {
fn from(value: GatewayEndpointConfigV1_1_20_2) -> Self {
GatewayEndpointConfig {
impl From<GatewayEndpointConfigV3> for GatewayEndpointConfigV5 {
fn from(value: GatewayEndpointConfigV3) -> Self {
GatewayEndpointConfigV5 {
gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
@@ -92,7 +105,7 @@ impl From<GatewayEndpointConfigV1_1_20_2> for GatewayEndpointConfig {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV1_1_20_2 {
pub struct ClientV3 {
pub version: String,
pub id: String,
@@ -105,12 +118,12 @@ pub struct ClientV1_1_20_2 {
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
pub gateway_endpoint: GatewayEndpointConfigV3,
}
impl From<ClientV1_1_20_2> for ClientV1_1_30 {
fn from(value: ClientV1_1_20_2) -> Self {
ClientV1_1_30 {
impl From<ClientV3> for ClientV4 {
fn from(value: ClientV3) -> Self {
ClientV4 {
version: value.version,
id: value.id,
disabled_credentials_mode: value.disabled_credentials_mode,
@@ -122,7 +135,7 @@ impl From<ClientV1_1_20_2> for ClientV1_1_30 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV1_1_20_2 {
pub struct TrafficV3 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
@@ -133,9 +146,9 @@ pub struct TrafficV1_1_20_2 {
pub packet_type: PacketType,
}
impl From<TrafficV1_1_20_2> for TrafficV1_1_30 {
fn from(value: TrafficV1_1_20_2) -> Self {
TrafficV1_1_30 {
impl From<TrafficV3> for TrafficV4 {
fn from(value: TrafficV3) -> Self {
TrafficV4 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -147,9 +160,9 @@ impl From<TrafficV1_1_20_2> for TrafficV1_1_30 {
}
}
impl Default for TrafficV1_1_20_2 {
impl Default for TrafficV3 {
fn default() -> Self {
TrafficV1_1_20_2 {
TrafficV3 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -162,16 +175,16 @@ impl Default for TrafficV1_1_20_2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV1_1_20_2 {
pub struct CoverTrafficV3 {
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
pub cover_traffic_primary_size_ratio: f64,
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTrafficV1_1_20_2> for CoverTrafficV1_1_30 {
fn from(value: CoverTrafficV1_1_20_2) -> Self {
CoverTrafficV1_1_30 {
impl From<CoverTrafficV3> for CoverTrafficV4 {
fn from(value: CoverTrafficV3) -> Self {
CoverTrafficV4 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
@@ -179,9 +192,9 @@ impl From<CoverTrafficV1_1_20_2> for CoverTrafficV1_1_30 {
}
}
impl Default for CoverTrafficV1_1_20_2 {
impl Default for CoverTrafficV3 {
fn default() -> Self {
CoverTrafficV1_1_20_2 {
CoverTrafficV3 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
@@ -191,22 +204,22 @@ impl Default for CoverTrafficV1_1_20_2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_20_2 {
pub struct GatewayConnectionV3 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV1_1_20_2> for GatewayConnectionV1_1_30 {
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
GatewayConnectionV1_1_30 {
impl From<GatewayConnectionV3> for GatewayConnectionV4 {
fn from(value: GatewayConnectionV3) -> Self {
GatewayConnectionV4 {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV1_1_20_2 {
impl Default for GatewayConnectionV3 {
fn default() -> Self {
GatewayConnectionV1_1_20_2 {
GatewayConnectionV3 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -214,7 +227,7 @@ impl Default for GatewayConnectionV1_1_20_2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_20_2 {
pub struct AcknowledgementsV3 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
@@ -222,9 +235,9 @@ pub struct AcknowledgementsV1_1_20_2 {
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV1_1_20_2> for AcknowledgementsV1_1_30 {
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
AcknowledgementsV1_1_30 {
impl From<AcknowledgementsV3> for AcknowledgementsV4 {
fn from(value: AcknowledgementsV3) -> Self {
AcknowledgementsV4 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
@@ -232,9 +245,9 @@ impl From<AcknowledgementsV1_1_20_2> for AcknowledgementsV1_1_30 {
}
}
impl Default for AcknowledgementsV1_1_20_2 {
impl Default for AcknowledgementsV3 {
fn default() -> Self {
AcknowledgementsV1_1_20_2 {
AcknowledgementsV3 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -244,7 +257,7 @@ impl Default for AcknowledgementsV1_1_20_2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_20_2 {
pub struct TopologyV3 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
@@ -252,9 +265,9 @@ pub struct TopologyV1_1_20_2 {
pub disable_refreshing: bool,
}
impl Default for TopologyV1_1_20_2 {
impl Default for TopologyV3 {
fn default() -> Self {
TopologyV1_1_20_2 {
TopologyV3 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
@@ -262,9 +275,9 @@ impl Default for TopologyV1_1_20_2 {
}
}
impl From<TopologyV1_1_20_2> for TopologyV1_1_30 {
fn from(value: TopologyV1_1_20_2) -> Self {
TopologyV1_1_30 {
impl From<TopologyV3> for TopologyV4 {
fn from(value: TopologyV3) -> Self {
TopologyV4 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
@@ -275,7 +288,7 @@ impl From<TopologyV1_1_20_2> for TopologyV1_1_30 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV1_1_20_2 {
pub struct ReplySurbsV3 {
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
@@ -291,9 +304,9 @@ pub struct ReplySurbsV1_1_20_2 {
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbsV1_1_20_2 {
impl Default for ReplySurbsV3 {
fn default() -> Self {
ReplySurbsV1_1_20_2 {
ReplySurbsV3 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -308,9 +321,9 @@ impl Default for ReplySurbsV1_1_20_2 {
}
}
impl From<ReplySurbsV1_1_20_2> for ReplySurbsV1_1_30 {
fn from(value: ReplySurbsV1_1_20_2) -> Self {
ReplySurbsV1_1_30 {
impl From<ReplySurbsV3> for ReplySurbsV4 {
fn from(value: ReplySurbsV3) -> Self {
ReplySurbsV4 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -327,18 +340,18 @@ impl From<ReplySurbsV1_1_20_2> for ReplySurbsV1_1_30 {
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1_1_20_2 {
pub traffic: TrafficV1_1_20_2,
pub cover_traffic: CoverTrafficV1_1_20_2,
pub gateway_connection: GatewayConnectionV1_1_20_2,
pub acknowledgements: AcknowledgementsV1_1_20_2,
pub topology: TopologyV1_1_20_2,
pub reply_surbs: ReplySurbsV1_1_20_2,
pub struct DebugConfigV3 {
pub traffic: TrafficV3,
pub cover_traffic: CoverTrafficV3,
pub gateway_connection: GatewayConnectionV3,
pub acknowledgements: AcknowledgementsV3,
pub topology: TopologyV3,
pub reply_surbs: ReplySurbsV3,
}
impl From<DebugConfigV1_1_20_2> for DebugConfigV1_1_30 {
fn from(value: DebugConfigV1_1_20_2) -> Self {
DebugConfigV1_1_30 {
impl From<DebugConfigV3> for DebugConfigV4 {
fn from(value: DebugConfigV3) -> Self {
DebugConfigV4 {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(),
@@ -1,15 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::topology_control::geo_aware_provider::CountryGroup;
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, GroupBy,
ReplySurbs, Topology, TopologyStructure, Traffic,
};
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
use crate::old::v5::{
AcknowledgementsV5, ClientV5, ConfigV5, CoverTrafficV5, DebugConfigV5, GatewayConnectionV5,
GroupByV5, ReplySurbsV5, TopologyStructureV5, TopologyV5, TrafficV5,
};
use crate::CountryGroup;
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -55,27 +53,39 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_30 = ConfigV4;
pub type ClientV1_1_30 = ClientV4;
pub type DebugConfigV1_1_30 = DebugConfigV4;
pub type TrafficV1_1_30 = TrafficV4;
pub type CoverTrafficV1_1_30 = CoverTrafficV4;
pub type GatewayConnectionV1_1_30 = GatewayConnectionV4;
pub type AcknowledgementsV1_1_30 = AcknowledgementsV4;
pub type TopologyV1_1_30 = TopologyV4;
pub type ReplySurbsV1_1_30 = ReplySurbsV4;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_30 {
pub client: ClientV1_1_30,
pub struct ConfigV4 {
pub client: ClientV4,
#[serde(default)]
pub debug: DebugConfigV1_1_30,
pub debug: DebugConfigV4,
}
impl From<ConfigV1_1_30> for Config {
fn from(value: ConfigV1_1_30) -> Self {
Config {
client: Client {
impl From<ConfigV4> for ConfigV5 {
fn from(value: ConfigV4) -> Self {
ConfigV5 {
client: ClientV5 {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfig {
traffic: Traffic {
debug: DebugConfigV5 {
traffic: TrafficV5 {
average_packet_delay: value.debug.traffic.average_packet_delay,
message_sending_average_delay: value
.debug
@@ -89,7 +99,7 @@ impl From<ConfigV1_1_30> for Config {
secondary_packet_size: value.debug.traffic.secondary_packet_size,
packet_type: value.debug.traffic.packet_type,
},
cover_traffic: CoverTraffic {
cover_traffic: CoverTrafficV5 {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
@@ -103,18 +113,18 @@ impl From<ConfigV1_1_30> for Config {
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnection {
gateway_connection: GatewayConnectionV5 {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
acknowledgements: AcknowledgementsV5 {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: Topology {
topology: TopologyV5 {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
@@ -124,7 +134,7 @@ impl From<ConfigV1_1_30> for Config {
.max_startup_gateway_waiting_period,
topology_structure: value.debug.topology.topology_structure.into(),
},
reply_surbs: ReplySurbs {
reply_surbs: ReplySurbsV5 {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
@@ -155,7 +165,10 @@ impl From<ConfigV1_1_30> for Config {
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.debug.reply_surbs.maximum_reply_surb_age,
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
// \/ ADDED
surb_mix_hops: None,
// /\ ADDED
},
},
}
@@ -165,7 +178,7 @@ impl From<ConfigV1_1_30> for Config {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct ClientV1_1_30 {
pub struct ClientV4 {
/// Version of the client for which this configuration was created.
pub version: String,
@@ -189,7 +202,7 @@ pub struct ClientV1_1_30 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TrafficV1_1_30 {
pub struct TrafficV4 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -220,9 +233,9 @@ pub struct TrafficV1_1_30 {
pub packet_type: PacketType,
}
impl Default for TrafficV1_1_30 {
impl Default for TrafficV4 {
fn default() -> Self {
TrafficV1_1_30 {
TrafficV4 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -235,7 +248,7 @@ impl Default for TrafficV1_1_30 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV1_1_30 {
pub struct CoverTrafficV4 {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
@@ -250,9 +263,9 @@ pub struct CoverTrafficV1_1_30 {
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTrafficV1_1_30 {
impl Default for CoverTrafficV4 {
fn default() -> Self {
CoverTrafficV1_1_30 {
CoverTrafficV4 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
@@ -262,16 +275,16 @@ impl Default for CoverTrafficV1_1_30 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_30 {
pub struct GatewayConnectionV4 {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnectionV1_1_30 {
impl Default for GatewayConnectionV4 {
fn default() -> Self {
GatewayConnectionV1_1_30 {
GatewayConnectionV4 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -279,7 +292,7 @@ impl Default for GatewayConnectionV1_1_30 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_30 {
pub struct AcknowledgementsV4 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -299,9 +312,9 @@ pub struct AcknowledgementsV1_1_30 {
pub ack_wait_addition: Duration,
}
impl Default for AcknowledgementsV1_1_30 {
impl Default for AcknowledgementsV4 {
fn default() -> Self {
AcknowledgementsV1_1_30 {
AcknowledgementsV4 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -311,7 +324,7 @@ impl Default for AcknowledgementsV1_1_30 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_30 {
pub struct TopologyV4 {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
@@ -334,23 +347,23 @@ pub struct TopologyV1_1_30 {
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructureV1_1_30,
pub topology_structure: TopologyStructureV4,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructureV1_1_30 {
pub enum TopologyStructureV4 {
#[default]
NymApi,
GeoAware(GroupByV1_1_30),
GeoAware(GroupByV4),
}
impl From<TopologyStructureV1_1_30> for TopologyStructure {
fn from(value: TopologyStructureV1_1_30) -> Self {
impl From<TopologyStructureV4> for TopologyStructureV5 {
fn from(value: TopologyStructureV4) -> Self {
match value {
TopologyStructureV1_1_30::NymApi => TopologyStructure::NymApi,
TopologyStructureV1_1_30::GeoAware(group_by) => {
TopologyStructure::GeoAware(group_by.into())
TopologyStructureV4::NymApi => TopologyStructureV5::NymApi,
TopologyStructureV4::GeoAware(group_by) => {
TopologyStructureV5::GeoAware(group_by.into())
}
}
}
@@ -358,44 +371,44 @@ impl From<TopologyStructureV1_1_30> for TopologyStructure {
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupByV1_1_30 {
pub enum GroupByV4 {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl From<GroupByV1_1_30> for GroupBy {
fn from(value: GroupByV1_1_30) -> Self {
impl From<GroupByV4> for GroupByV5 {
fn from(value: GroupByV4) -> Self {
match value {
GroupByV1_1_30::CountryGroup(country) => GroupBy::CountryGroup(country),
GroupByV1_1_30::NymAddress(addr) => GroupBy::NymAddress(addr),
GroupByV4::CountryGroup(country) => GroupByV5::CountryGroup(country),
GroupByV4::NymAddress(addr) => GroupByV5::NymAddress(addr),
}
}
}
impl std::fmt::Display for GroupByV1_1_30 {
impl std::fmt::Display for GroupByV4 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupByV1_1_30::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV1_1_30::NymAddress(address) => write!(f, "address: {}", address),
GroupByV4::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV4::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for TopologyV1_1_30 {
impl Default for TopologyV4 {
fn default() -> Self {
TopologyV1_1_30 {
TopologyV4 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructureV1_1_30::default(),
topology_structure: TopologyStructureV4::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV1_1_30 {
pub struct ReplySurbsV4 {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -433,9 +446,9 @@ pub struct ReplySurbsV1_1_30 {
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbsV1_1_30 {
impl Default for ReplySurbsV4 {
fn default() -> Self {
ReplySurbsV1_1_30 {
ReplySurbsV4 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -452,22 +465,22 @@ impl Default for ReplySurbsV1_1_30 {
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1_1_30 {
pub struct DebugConfigV4 {
/// Defines all configuration options related to traffic streams.
pub traffic: TrafficV1_1_30,
pub traffic: TrafficV4,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficV1_1_30,
pub cover_traffic: CoverTrafficV4,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionV1_1_30,
pub gateway_connection: GatewayConnectionV4,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsV1_1_30,
pub acknowledgements: AcknowledgementsV4,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyV1_1_30,
pub topology: TopologyV4,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV1_1_30,
pub reply_surbs: ReplySurbsV4,
}
@@ -0,0 +1,499 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
Acknowledgements, Client, Config, CountryGroup, CoverTraffic, DebugConfig, GatewayConnection,
GroupBy, ReplySurbs, Topology, TopologyStructure, Traffic,
};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_33 = ConfigV5;
pub type ClientV1_1_33 = ClientV5;
pub type DebugConfigV1_1_33 = DebugConfigV5;
pub type OldGatewayEndpointConfigV1_1_33 = GatewayEndpointConfigV5;
pub type TrafficV1_1_33 = TrafficV5;
pub type CoverTrafficV1_1_33 = CoverTrafficV5;
pub type GatewayConnectionV1_1_33 = GatewayConnectionV5;
pub type AcknowledgementsV1_1_33 = AcknowledgementsV5;
pub type TopologyV1_1_33 = TopologyV5;
pub type ReplySurbsV1_1_33 = ReplySurbsV5;
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV5 {
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
/// Address of the gateway owner to which the client should send messages.
pub gateway_owner: String,
/// Address of the gateway listener to which all client requests should be sent.
pub gateway_listener: String,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV5 {
pub client: ClientV5,
#[serde(default)]
pub debug: DebugConfigV5,
}
impl From<ConfigV5> for Config {
fn from(value: ConfigV5) -> Self {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfig {
traffic: Traffic {
average_packet_delay: value.debug.traffic.average_packet_delay,
message_sending_average_delay: value
.debug
.traffic
.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.debug
.traffic
.disable_main_poisson_packet_distribution,
primary_packet_size: value.debug.traffic.primary_packet_size,
secondary_packet_size: value.debug.traffic.secondary_packet_size,
packet_type: value.debug.traffic.packet_type,
},
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value
.debug
.cover_traffic
.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value
.debug
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnection {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: Topology {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
max_startup_gateway_waiting_period: value
.debug
.topology
.max_startup_gateway_waiting_period,
topology_structure: value.debug.topology.topology_structure.into(),
},
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value
.debug
.reply_surbs
.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.debug.reply_surbs.maximum_reply_surb_age,
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
},
},
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct ClientV5 {
/// Version of the client for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TrafficV5 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity.
/// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
}
impl Default for TrafficV5 {
fn default() -> Self {
TrafficV5 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV5 {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
/// Only applicable if `secondary_packet_size` is enabled.
pub cover_traffic_primary_size_ratio: f64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTrafficV5 {
fn default() -> Self {
CoverTrafficV5 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV5 {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnectionV5 {
fn default() -> Self {
GatewayConnectionV5 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV5 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for AcknowledgementsV5 {
fn default() -> Self {
AcknowledgementsV5 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV5 {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructureV5,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructureV5 {
#[default]
NymApi,
GeoAware(GroupByV5),
}
impl From<TopologyStructureV5> for TopologyStructure {
fn from(value: TopologyStructureV5) -> Self {
match value {
TopologyStructureV5::NymApi => TopologyStructure::NymApi,
TopologyStructureV5::GeoAware(group_by) => TopologyStructure::GeoAware(group_by.into()),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupByV5 {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl From<GroupByV5> for GroupBy {
fn from(value: GroupByV5) -> Self {
match value {
GroupByV5::CountryGroup(country) => GroupBy::CountryGroup(country),
GroupByV5::NymAddress(addr) => GroupBy::NymAddress(addr),
}
}
}
impl std::fmt::Display for GroupByV5 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupByV5::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV5::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for TopologyV5 {
fn default() -> Self {
TopologyV5 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructureV5::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV5 {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
pub maximum_reply_surb_storage_threshold: usize,
/// Defines the minimum number of reply surbs the client would request.
pub minimum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs the client would request.
pub maximum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
pub maximum_allowed_reply_surb_request_size: u32,
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
/// for more even though in theory they wouldn't need to.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
/// Defines maximum amount of time the client is going to wait for reply surbs before
/// deciding it's never going to get them and would drop all pending messages
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
/// Defines maximum amount of time given reply surb is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
/// Defines maximum amount of time given reply key is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
/// the default value is used.
pub surb_mix_hops: Option<u8>,
}
impl Default for ReplySurbsV5 {
fn default() -> Self {
ReplySurbsV5 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
surb_mix_hops: None,
}
}
}
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV5 {
/// Defines all configuration options related to traffic streams.
pub traffic: TrafficV5,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficV5,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionV5,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsV5,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyV5,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV5,
}
@@ -0,0 +1,33 @@
[package]
name = "nym-client-core-gateways-storage"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait.workspace = true
cosmrs.workspace = true
log.workspace = true
serde = { workspace = true, features = ["derive"] }
thiserror.workspace = true
time.workspace = true
tokio = { workspace = true, features = ["sync"] }
url.workspace = true
zeroize = { workspace = true, features = ["zeroize_derive"] }
nym-crypto = { path = "../../crypto", features = ["asymmetric"] }
nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"]
optional = true
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
fs-gateways-storage = ["sqlx"]
@@ -0,0 +1,31 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[tokio::main]
async fn main() {
#[cfg(feature = "fs-gateways-storage")]
{
use sqlx::{Connection, SqliteConnection};
use std::env;
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/gateways-storage-example.sqlite");
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./fs_gateways_migrations")
.run(&mut conn)
.await
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
#[cfg(target_family = "windows")]
// for some strange reason we need to add a leading `/` to the windows path even though it's
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
CREATE TABLE active_gateway
(
id INTEGER PRIMARY KEY CHECK (id = 0),
active_gateway_id_bs58 TEXT REFERENCES registered_gateway (gateway_id_bs58)
);
CREATE TABLE registered_gateway
(
gateway_id_bs58 TEXT NOT NULL UNIQUE PRIMARY KEY,
registration_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
gateway_type TEXT CHECK ( gateway_type IN ('remote', 'custom') ) NOT NULL DEFAULT 'remote'
);
-- TODO: perhaps keep additional metadata such as bandwidth, credential usage, etc
CREATE TABLE remote_gateway_details
(
gateway_id_bs58 TEXT NOT NULL UNIQUE PRIMARY KEY REFERENCES registered_gateway (gateway_id_bs58),
derived_aes128_ctr_blake3_hmac_keys_bs58 TEXT NOT NULL,
gateway_owner_address TEXT NOT NULL,
gateway_listener TEXT NOT NULL,
wg_tun_address TEXT
);
CREATE TABLE custom_gateway_details
(
gateway_id_bs58 TEXT NOT NULL UNIQUE PRIMARY KEY REFERENCES registered_gateway (gateway_id_bs58),
data BLOB
);
INSERT INTO active_gateway(id, active_gateway_id_bs58)
values (0, NULL);
@@ -0,0 +1,45 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::BadGateway;
use std::io;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum StorageError {
#[error("the provided database path doesn't have a filename defined")]
DatabasePathWithoutFilename { provided_path: PathBuf },
#[error("unable to create the directory for the database at {}: {source}", provided_path.display())]
DatabasePathUnableToCreateParentDirectory {
provided_path: PathBuf,
source: io::Error,
},
#[error("failed to perform sqlx migration: {source}")]
MigrationError {
#[source]
#[from]
source: sqlx::migrate::MigrateError,
},
#[error("failed to connect to the underlying connection pool: {source}")]
DatabaseConnectionError {
#[source]
source: sqlx::error::Error,
},
#[error("failed to run the SQL query: {source}")]
QueryError {
#[source]
#[from]
source: sqlx::error::Error,
},
#[error(transparent)]
MalformedGateway(#[from] BadGateway),
#[error("gateway {gateway_id} does not exist in the storage")]
GatewayDoesNotExist { gateway_id: String },
}
@@ -0,0 +1,234 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
backend::fs_backend::error::StorageError,
types::{
RawActiveGateway, RawCustomGatewayDetails, RawRegisteredGateway, RawRemoteGatewayDetails,
},
};
use log::{debug, error};
use sqlx::ConnectOptions;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct StorageManager {
pub connection_pool: sqlx::SqlitePool,
}
// all SQL goes here
impl StorageManager {
pub async fn init<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
// ensure the whole directory structure exists
if let Some(parent_dir) = database_path.as_ref().parent() {
std::fs::create_dir_all(parent_dir).map_err(|source| {
StorageError::DatabasePathUnableToCreateParentDirectory {
provided_path: database_path.as_ref().to_path_buf(),
source,
}
})?;
}
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
.filename(database_path)
.create_if_missing(true);
opts.disable_statement_logging();
let connection_pool = sqlx::SqlitePool::connect_with(opts)
.await
.map_err(|source| {
error!("Failed to connect to SQLx database: {source}");
StorageError::DatabaseConnectionError { source }
})?;
sqlx::migrate!("./fs_gateways_migrations")
.run(&connection_pool)
.await
.inspect_err(|err| {
error!("Failed to initialize SQLx database: {err}");
})?;
debug!("Database migration finished!");
Ok(StorageManager { connection_pool })
}
pub(crate) async fn get_active_gateway(&self) -> Result<RawActiveGateway, sqlx::Error> {
sqlx::query_as!(
RawActiveGateway,
"SELECT active_gateway_id_bs58 FROM active_gateway"
)
.fetch_one(&self.connection_pool)
.await
}
pub(crate) async fn set_active_gateway(
&self,
gateway_id: Option<&str>,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"UPDATE active_gateway SET active_gateway_id_bs58 = ?",
gateway_id
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn has_registered_gateway(
&self,
gateway_id: &str,
) -> Result<bool, sqlx::Error> {
sqlx::query!("SELECT EXISTS (SELECT 1 FROM registered_gateway WHERE gateway_id_bs58 = ?) AS 'exists'", gateway_id)
.fetch_one(&self.connection_pool)
.await
.map(|result| result.exists == 1)
}
pub(crate) async fn maybe_get_registered_gateway(
&self,
gateway_id: &str,
) -> Result<Option<RawRegisteredGateway>, sqlx::Error> {
sqlx::query_as("SELECT * FROM registered_gateway WHERE gateway_id_bs58 = ?")
.bind(gateway_id)
.fetch_optional(&self.connection_pool)
.await
}
pub(crate) async fn must_get_registered_gateway(
&self,
gateway_id: &str,
) -> Result<RawRegisteredGateway, sqlx::Error> {
sqlx::query_as("SELECT * FROM registered_gateway WHERE gateway_id_bs58 = ?")
.bind(gateway_id)
.fetch_one(&self.connection_pool)
.await
}
pub(crate) async fn set_registered_gateway(
&self,
registered_gateway: &RawRegisteredGateway,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type)
VALUES (?, ?, ?)
"#,
registered_gateway.gateway_id_bs58,
registered_gateway.registration_timestamp,
registered_gateway.gateway_type,
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn remove_registered_gateway(
&self,
gateway_id: &str,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"DELETE FROM registered_gateway WHERE gateway_id_bs58 = ?",
gateway_id
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn get_remote_gateway_details(
&self,
gateway_id: &str,
) -> Result<RawRemoteGatewayDetails, sqlx::Error> {
sqlx::query_as!(
RawRemoteGatewayDetails,
"SELECT * FROM remote_gateway_details WHERE gateway_id_bs58 = ?",
gateway_id
)
.fetch_one(&self.connection_pool)
.await
}
pub(crate) async fn set_remote_gateway_details(
&self,
remote: &RawRemoteGatewayDetails,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58, gateway_owner_address, gateway_listener, wg_tun_address)
VALUES (?, ?, ?, ?, ?)
"#,
remote.gateway_id_bs58,
remote.derived_aes128_ctr_blake3_hmac_keys_bs58,
remote.gateway_owner_address,
remote.gateway_listener,
remote.wg_tun_address,
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn remove_remote_gateway_details(
&self,
gateway_id: &str,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"DELETE FROM remote_gateway_details WHERE gateway_id_bs58 = ?",
gateway_id
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn get_custom_gateway_details(
&self,
gateway_id: &str,
) -> Result<RawCustomGatewayDetails, sqlx::Error> {
sqlx::query_as!(
RawCustomGatewayDetails,
"SELECT * FROM custom_gateway_details WHERE gateway_id_bs58 = ?",
gateway_id
)
.fetch_one(&self.connection_pool)
.await
}
pub(crate) async fn set_custom_gateway_details(
&self,
custom: &RawCustomGatewayDetails,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO custom_gateway_details(gateway_id_bs58, data)
VALUES (?, ?)
"#,
custom.gateway_id_bs58,
custom.data,
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn remove_custom_gateway_details(
&self,
gateway_id: &str,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"DELETE FROM custom_gateway_details WHERE gateway_id_bs58 = ?",
gateway_id
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn registered_gateways(&self) -> Result<Vec<String>, sqlx::Error> {
sqlx::query!("SELECT gateway_id_bs58 FROM registered_gateway")
.fetch_all(&self.connection_pool)
.await
.map(|records| records.into_iter().map(|r| r.gateway_id_bs58).collect())
}
}
@@ -0,0 +1,155 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
ActiveGateway, BadGateway, GatewayDetails, GatewayRegistration, GatewayType,
GatewaysDetailsStore, StorageError,
};
use async_trait::async_trait;
use manager::StorageManager;
use nym_crypto::asymmetric::identity::PublicKey;
use std::path::Path;
pub mod error;
mod manager;
mod models;
pub struct OnDiskGatewaysDetails {
manager: StorageManager,
}
impl OnDiskGatewaysDetails {
pub async fn init<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
Ok(OnDiskGatewaysDetails {
manager: StorageManager::init(database_path).await?,
})
}
}
#[async_trait]
impl GatewaysDetailsStore for OnDiskGatewaysDetails {
type StorageError = error::StorageError;
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
Ok(self
.manager
.maybe_get_registered_gateway(gateway_id)
.await?
.is_some())
}
async fn active_gateway(&self) -> Result<ActiveGateway, Self::StorageError> {
let raw_active = self.manager.get_active_gateway().await?;
let registration = match raw_active.active_gateway_id_bs58 {
None => None,
Some(gateway_id) => Some(self.load_gateway_details(&gateway_id).await?),
};
Ok(ActiveGateway { registration })
}
async fn set_active_gateway(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
if !self.manager.has_registered_gateway(gateway_id).await? {
return Err(StorageError::GatewayDoesNotExist {
gateway_id: gateway_id.to_string(),
});
}
Ok(self.manager.set_active_gateway(Some(gateway_id)).await?)
}
async fn all_gateways(&self) -> Result<Vec<GatewayRegistration>, Self::StorageError> {
let identities = self.manager.registered_gateways().await?;
let mut registered = Vec::with_capacity(identities.len());
for gateway_id in identities {
registered.push(self.load_gateway_details(&gateway_id).await?)
}
Ok(registered)
}
async fn all_gateways_identities(&self) -> Result<Vec<PublicKey>, Self::StorageError> {
Ok(self
.manager
.registered_gateways()
.await?
.into_iter()
.map(|gateway_id| {
gateway_id
.as_str()
.parse()
.map_err(|source| BadGateway::MalformedGatewayIdentity { gateway_id, source })
})
.collect::<Result<_, _>>()?)
}
async fn load_gateway_details(
&self,
gateway_id: &str,
) -> Result<GatewayRegistration, Self::StorageError> {
let raw_registration = self.manager.must_get_registered_gateway(gateway_id).await?;
let typ: GatewayType = raw_registration.gateway_type.parse()?;
let details = match typ {
GatewayType::Remote => {
let raw_details = self.manager.get_remote_gateway_details(gateway_id).await?;
GatewayDetails::Remote(raw_details.try_into()?)
}
GatewayType::Custom => {
let raw_details = self.manager.get_custom_gateway_details(gateway_id).await?;
GatewayDetails::Custom(raw_details.try_into()?)
}
};
Ok(GatewayRegistration {
details,
registration_timestamp: raw_registration.registration_timestamp,
})
}
async fn store_gateway_details(
&self,
details: &GatewayRegistration,
) -> Result<(), Self::StorageError> {
let raw_registration = details.into();
self.manager
.set_registered_gateway(&raw_registration)
.await?;
match &details.details {
GatewayDetails::Remote(remote_details) => {
let raw_details = remote_details.into();
self.manager
.set_remote_gateway_details(&raw_details)
.await?;
}
GatewayDetails::Custom(custom_details) => {
let raw_details = custom_details.into();
self.manager
.set_custom_gateway_details(&raw_details)
.await?;
}
}
Ok(())
}
// ideally all of those should be run under a storage tx to ensure storage consistency,
// but at that point it's fine
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
let active = self.manager.get_active_gateway().await?;
if let Some(currently_active) = &active.active_gateway_id_bs58 {
if currently_active == gateway_id {
self.manager.set_active_gateway(None).await?;
}
}
// just try remove it from all tables even if it doesn't actually exist
self.manager.remove_registered_gateway(gateway_id).await?;
self.manager
.remove_remote_gateway_details(gateway_id)
.await?;
self.manager
.remove_custom_gateway_details(gateway_id)
.await?;
Ok(())
}
}
@@ -0,0 +1,2 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
@@ -0,0 +1,108 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::types::{ActiveGateway, GatewayRegistration};
use crate::{BadGateway, GatewaysDetailsStore};
use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use thiserror::Error;
use tokio::sync::RwLock;
#[derive(Debug, Error)]
pub enum InMemStorageError {
#[error("gateway {gateway_id} does not exist")]
GatewayDoesNotExist { gateway_id: String },
#[error(transparent)]
MalformedGateway(#[from] BadGateway),
}
#[derive(Debug, Default)]
pub struct InMemGatewaysDetails {
inner: Arc<RwLock<InMemStorageInner>>,
}
#[derive(Debug, Default)]
struct InMemStorageInner {
active_gateway: Option<String>,
gateways: HashMap<String, GatewayRegistration>,
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewaysDetailsStore for InMemGatewaysDetails {
type StorageError = InMemStorageError;
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
Ok(self.inner.read().await.gateways.contains_key(gateway_id))
}
async fn active_gateway(&self) -> Result<ActiveGateway, Self::StorageError> {
let guard = self.inner.read().await;
let registration = guard.active_gateway.as_ref().map(|id| {
// SAFETY: if particular gateway is set as active, its details MUST exist
#[allow(clippy::unwrap_used)]
guard.gateways.get(id).unwrap().clone()
});
Ok(ActiveGateway { registration })
}
async fn set_active_gateway(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
// ensure the gateway with provided id exists
let mut guard = self.inner.write().await;
if !guard.gateways.contains_key(gateway_id) {
return Err(InMemStorageError::GatewayDoesNotExist {
gateway_id: gateway_id.to_string(),
});
}
guard.active_gateway = Some(gateway_id.to_string());
Ok(())
}
async fn all_gateways(&self) -> Result<Vec<GatewayRegistration>, Self::StorageError> {
Ok(self.inner.read().await.gateways.values().cloned().collect())
}
async fn load_gateway_details(
&self,
gateway_id: &str,
) -> Result<GatewayRegistration, Self::StorageError> {
self.inner
.read()
.await
.gateways
.get(gateway_id)
.cloned()
.ok_or(InMemStorageError::GatewayDoesNotExist {
gateway_id: gateway_id.to_string(),
})
}
async fn store_gateway_details(
&self,
details: &GatewayRegistration,
) -> Result<(), Self::StorageError> {
self.inner.write().await.gateways.insert(
details.details.gateway_id().to_base58_string(),
details.clone(),
);
Ok(())
}
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
let mut guard = self.inner.write().await;
if let Some(active) = guard.active_gateway.as_ref() {
if active == gateway_id {
guard.active_gateway = None
}
}
guard.gateways.remove(gateway_id);
Ok(())
}
}
@@ -0,0 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-gateways-storage"))]
pub mod fs_backend;
pub mod mem_backend;
@@ -0,0 +1,50 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_requests::registration::handshake::shared_key::SharedKeyConversionError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BadGateway {
#[error("{typ} is not a valid gateway type")]
InvalidGatewayType { typ: String },
#[error("the provided gateway identity {gateway_id} is malformed: {source}")]
MalformedGatewayIdentity {
gateway_id: String,
#[source]
source: Ed25519RecoveryError,
},
#[error("the account owner of gateway {gateway_id} ({raw_owner}) is malformed: {source}")]
MalformedGatewayOwnerAccountAddress {
gateway_id: String,
raw_owner: String,
#[source]
source: cosmrs::ErrorReport,
},
#[error("the shared keys provided for gateway {gateway_id} are malformed: {source}")]
MalformedSharedKeys {
gateway_id: String,
#[source]
source: SharedKeyConversionError,
},
#[error(
"the listening address of gateway {gateway_id} ({raw_listener}) is malformed: {source}"
)]
MalformedListener {
gateway_id: String,
raw_listener: String,
#[source]
source: url::ParseError,
},
}
@@ -0,0 +1,66 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
use async_trait::async_trait;
use std::error::Error;
pub mod backend;
pub mod error;
pub mod types;
// todo: export port types
pub use crate::types::*;
pub use backend::mem_backend::{InMemGatewaysDetails, InMemStorageError};
pub use error::BadGateway;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-gateways-storage"))]
pub use backend::fs_backend::{error::StorageError, OnDiskGatewaysDetails};
use nym_crypto::asymmetric::identity;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewaysDetailsStore {
type StorageError: Error + From<error::BadGateway>;
/// Returns details of the currently active gateway, if available.
async fn active_gateway(&self) -> Result<ActiveGateway, Self::StorageError>;
/// Set the provided gateway as the currently active gateway.
async fn set_active_gateway(&self, gateway_id: &str) -> Result<(), Self::StorageError>;
/// Returns details of all registered gateways.
async fn all_gateways(&self) -> Result<Vec<GatewayRegistration>, Self::StorageError>;
/// Return identity keys of all registered gateways.
async fn all_gateways_identities(
&self,
) -> Result<Vec<identity::PublicKey>, Self::StorageError> {
Ok(self
.all_gateways()
.await?
.into_iter()
.map(|gateway| gateway.details.gateway_id())
.collect())
}
/// Check if the gateway with the provided id already exists in the store.
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError>;
/// Returns details of the particular gateway.
async fn load_gateway_details(
&self,
gateway_id: &str,
) -> Result<GatewayRegistration, Self::StorageError>;
/// Store the provided gateway details.
async fn store_gateway_details(
&self,
details: &GatewayRegistration,
) -> Result<(), Self::StorageError>;
/// Remove given gateway details from the underlying store.
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError>;
}
@@ -0,0 +1,315 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::BadGateway;
use cosmrs::AccountId;
use nym_crypto::asymmetric::identity;
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use std::sync::Arc;
use time::OffsetDateTime;
use url::Url;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub const REMOTE_GATEWAY_TYPE: &str = "remote";
pub const CUSTOM_GATEWAY_TYPE: &str = "custom";
#[derive(Debug, Clone, Default)]
pub struct ActiveGateway {
pub registration: Option<GatewayRegistration>,
}
#[derive(Debug, Clone)]
pub struct GatewayRegistration {
pub details: GatewayDetails,
pub registration_timestamp: OffsetDateTime,
}
impl GatewayRegistration {
pub fn gateway_id(&self) -> identity::PublicKey {
self.details.gateway_id()
}
}
impl<'a> From<&'a GatewayRegistration> for RawRegisteredGateway {
fn from(value: &'a GatewayRegistration) -> Self {
RawRegisteredGateway {
gateway_id_bs58: value.details.gateway_id().to_base58_string(),
registration_timestamp: value.registration_timestamp,
gateway_type: value.details.typ().to_string(),
}
}
}
#[derive(Debug, Clone)]
pub enum GatewayDetails {
/// Standard details of a remote gateway
Remote(RemoteGatewayDetails),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(CustomGatewayDetails),
}
impl From<GatewayDetails> for GatewayRegistration {
fn from(details: GatewayDetails) -> Self {
GatewayRegistration {
details,
registration_timestamp: OffsetDateTime::now_utc(),
}
}
}
impl GatewayDetails {
pub fn new_remote(
gateway_id: identity::PublicKey,
derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>,
gateway_owner_address: AccountId,
gateway_listener: Url,
wg_tun_address: Option<Url>,
) -> Self {
GatewayDetails::Remote(RemoteGatewayDetails {
gateway_id,
derived_aes128_ctr_blake3_hmac_keys,
gateway_owner_address,
gateway_listener,
wg_tun_address,
})
}
pub fn new_custom(gateway_id: identity::PublicKey, data: Option<Vec<u8>>) -> Self {
GatewayDetails::Custom(CustomGatewayDetails { gateway_id, data })
}
pub fn gateway_id(&self) -> identity::PublicKey {
match self {
GatewayDetails::Remote(details) => details.gateway_id,
GatewayDetails::Custom(details) => details.gateway_id,
}
}
pub fn shared_key(&self) -> Option<&SharedKeys> {
match self {
GatewayDetails::Remote(details) => Some(&details.derived_aes128_ctr_blake3_hmac_keys),
GatewayDetails::Custom(_) => None,
}
}
pub fn is_custom(&self) -> bool {
matches!(self, GatewayDetails::Custom(..))
}
pub fn typ(&self) -> GatewayType {
match self {
GatewayDetails::Remote(_) => GatewayType::Remote,
GatewayDetails::Custom(_) => GatewayType::Custom,
}
}
}
#[derive(Debug, Copy, Clone, Default)]
pub enum GatewayType {
#[default]
Remote,
Custom,
}
impl FromStr for GatewayType {
type Err = BadGateway;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
REMOTE_GATEWAY_TYPE => Ok(GatewayType::Remote),
CUSTOM_GATEWAY_TYPE => Ok(GatewayType::Custom),
other => Err(BadGateway::InvalidGatewayType {
typ: other.to_string(),
}),
}
}
}
impl Display for GatewayType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
GatewayType::Remote => REMOTE_GATEWAY_TYPE.fmt(f),
GatewayType::Custom => CUSTOM_GATEWAY_TYPE.fmt(f),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct RawActiveGateway {
pub active_gateway_id_bs58: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct RawRegisteredGateway {
pub gateway_id_bs58: String,
// not necessarily needed but is nice for display purposes
pub registration_timestamp: OffsetDateTime,
pub gateway_type: String,
}
#[derive(Debug, Clone, Copy)]
pub struct RegisteredGateway {
pub gateway_id: identity::PublicKey,
pub registration_timestamp: OffsetDateTime,
pub gateway_type: GatewayType,
}
#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct RawRemoteGatewayDetails {
pub gateway_id_bs58: String,
pub derived_aes128_ctr_blake3_hmac_keys_bs58: String,
pub gateway_owner_address: String,
pub gateway_listener: String,
pub wg_tun_address: Option<String>,
}
impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
type Error = BadGateway;
fn try_from(value: RawRemoteGatewayDetails) -> Result<Self, Self::Error> {
let gateway_id =
identity::PublicKey::from_base58_string(&value.gateway_id_bs58).map_err(|source| {
BadGateway::MalformedGatewayIdentity {
gateway_id: value.gateway_id_bs58.clone(),
source,
}
})?;
let derived_aes128_ctr_blake3_hmac_keys = Arc::new(
SharedKeys::try_from_base58_string(&value.derived_aes128_ctr_blake3_hmac_keys_bs58)
.map_err(|source| BadGateway::MalformedSharedKeys {
gateway_id: value.gateway_id_bs58.clone(),
source,
})?,
);
let gateway_owner_address =
AccountId::from_str(&value.gateway_owner_address).map_err(|source| {
BadGateway::MalformedGatewayOwnerAccountAddress {
gateway_id: value.gateway_id_bs58.clone(),
raw_owner: value.gateway_owner_address.clone(),
source,
}
})?;
let gateway_listener = Url::parse(&value.gateway_listener).map_err(|source| {
BadGateway::MalformedListener {
gateway_id: value.gateway_id_bs58.clone(),
raw_listener: value.gateway_listener.clone(),
source,
}
})?;
let wg_tun_address = value
.wg_tun_address
.as_ref()
.map(|addr| {
Url::parse(addr).map_err(|source| BadGateway::MalformedListener {
gateway_id: value.gateway_id_bs58.clone(),
raw_listener: addr.clone(),
source,
})
})
.transpose()?;
Ok(RemoteGatewayDetails {
gateway_id,
derived_aes128_ctr_blake3_hmac_keys,
gateway_owner_address,
gateway_listener,
wg_tun_address,
})
}
}
impl<'a> From<&'a RemoteGatewayDetails> for RawRemoteGatewayDetails {
fn from(value: &'a RemoteGatewayDetails) -> Self {
RawRemoteGatewayDetails {
gateway_id_bs58: value.gateway_id.to_base58_string(),
derived_aes128_ctr_blake3_hmac_keys_bs58: value
.derived_aes128_ctr_blake3_hmac_keys
.to_base58_string(),
gateway_owner_address: value.gateway_owner_address.to_string(),
gateway_listener: value.gateway_listener.to_string(),
wg_tun_address: value.wg_tun_address.as_ref().map(|addr| addr.to_string()),
}
}
}
#[derive(Debug, Clone)]
pub struct RemoteGatewayDetails {
pub gateway_id: identity::PublicKey,
// note: `SharedKeys` implement ZeroizeOnDrop, meaning when `RemoteGatewayDetails` is dropped,
// the keys will be zeroized
pub derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>,
pub gateway_owner_address: AccountId,
pub gateway_listener: Url,
pub wg_tun_address: Option<Url>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct RawCustomGatewayDetails {
pub gateway_id_bs58: String,
pub data: Option<Vec<u8>>,
}
impl TryFrom<RawCustomGatewayDetails> for CustomGatewayDetails {
type Error = BadGateway;
fn try_from(value: RawCustomGatewayDetails) -> Result<Self, Self::Error> {
let gateway_id =
identity::PublicKey::from_base58_string(&value.gateway_id_bs58).map_err(|source| {
BadGateway::MalformedGatewayIdentity {
gateway_id: value.gateway_id_bs58.clone(),
source,
}
})?;
Ok(CustomGatewayDetails {
gateway_id,
data: value.data,
})
}
}
impl<'a> From<&'a CustomGatewayDetails> for RawCustomGatewayDetails {
fn from(value: &'a CustomGatewayDetails) -> Self {
RawCustomGatewayDetails {
gateway_id_bs58: value.gateway_id.to_base58_string(),
// I don't know what to feel about that clone here given it might contain possibly sensitive data
data: value.data.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct CustomGatewayDetails {
pub gateway_id: identity::PublicKey,
pub data: Option<Vec<u8>>,
}
impl CustomGatewayDetails {
pub fn new(gateway_id: identity::PublicKey) -> CustomGatewayDetails {
Self {
gateway_id,
data: None,
}
}
}
@@ -0,0 +1,162 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::types::GatewayInfo;
use crate::cli_helpers::{CliClient, CliClientConfig};
use crate::client::base_client::non_wasm_helpers::setup_fs_gateways_storage;
use crate::{
client::{
base_client::storage::helpers::{get_all_registered_identities, set_active_gateway},
key_manager::persistence::OnDiskKeys,
},
error::ClientCoreError,
init::types::{GatewaySelectionSpecification, GatewaySetup},
};
use log::info;
use nym_client_core_gateways_storage::GatewayDetails;
use nym_crypto::asymmetric::identity;
use nym_topology::NymTopology;
use std::path::PathBuf;
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[derive(Debug, Clone)]
pub struct CommonClientAddGatewayArgs {
/// Id of client we want to add gateway for.
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Explicitly specify id of the gateway to register with.
/// If unspecified, a random gateway will be chosen instead.
#[cfg_attr(feature = "cli", clap(long, alias = "gateway"))]
pub gateway_id: Option<identity::PublicKey>,
/// Specifies whether the client will attempt to enforce tls connection to the desired gateway.
#[cfg_attr(feature = "cli", clap(long))]
pub force_tls_gateway: bool,
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
/// uniformly.
#[cfg_attr(feature = "cli", clap(long, conflicts_with = "gateway_id"))]
pub latency_based_selection: bool,
/// Specify whether this new gateway should be set as the active one
#[cfg_attr(feature = "cli", clap(long, default_value_t = true))]
pub set_active: bool,
/// Comma separated list of rest endpoints of the API validators
#[cfg_attr(
feature = "cli",
clap(
long,
alias = "api_validators",
value_delimiter = ',',
group = "network"
)
)]
pub nym_apis: Option<Vec<url::Url>>,
/// Path to .json file containing custom network specification.
#[cfg_attr(feature = "cli", clap(long, group = "network", hide = true))]
pub custom_mixnet: Option<PathBuf>,
}
pub async fn add_gateway<C, A>(args: A) -> Result<GatewayInfo, C::Error>
where
A: AsRef<CommonClientAddGatewayArgs>,
C: CliClient,
{
let common_args = args.as_ref();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let core = config.core_config();
let paths = config.common_paths();
let key_store = OnDiskKeys::new(paths.keys.clone());
let details_store = setup_fs_gateways_storage(&paths.gateway_registrations).await?;
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = common_args.gateway_id;
log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}");
let selection_spec = GatewaySelectionSpecification::new(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(common_args.latency_based_selection),
common_args.force_tls_gateway,
);
log::debug!("Gateway selection specification: {selection_spec:?}");
let registered_gateways = get_all_registered_identities(&details_store).await?;
// if user provided gateway id (and we can't overwrite data), make sure we're not trying to register
// with a known gateway
if let Some(user_chosen) = user_chosen_gateway_id {
if registered_gateways.contains(&user_chosen) {
return Err(ClientCoreError::AlreadyRegistered {
gateway_id: user_chosen.to_base58_string(),
}
.into());
}
}
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let available_gateways = if let Some(custom_mixnet) = common_args.custom_mixnet.as_ref() {
let hardcoded_topology = NymTopology::new_from_file(custom_mixnet).map_err(|source| {
ClientCoreError::CustomTopologyLoadFailure {
file_path: custom_mixnet.clone(),
source,
}
})?;
hardcoded_topology.get_gateways()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await?
};
// since we're registering with a brand new gateway,
// make sure the list of available gateways doesn't overlap the list of known gateways
let available_gateways = available_gateways
.into_iter()
.filter(|g| !registered_gateways.contains(g.identity()))
.collect::<Vec<_>>();
if available_gateways.is_empty() {
return Err(ClientCoreError::NoNewGatewaysAvailable.into());
}
let gateway_setup = GatewaySetup::New {
specification: selection_spec,
available_gateways,
wg_tun_address: None,
};
let init_details =
crate::init::setup_gateway(gateway_setup, &key_store, &details_store).await?;
let address = init_details.client_address();
let gateway_registration = init_details.gateway_registration;
let GatewayDetails::Remote(ref gateway_details) = gateway_registration.details else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
};
if common_args.set_active {
set_active_gateway(
&details_store,
&gateway_details.gateway_id.to_base58_string(),
)
.await?;
} else {
info!("registered with new gateway {} (under address {address}), but this will not be our default address", gateway_details.gateway_id);
}
Ok(GatewayInfo {
registration: gateway_registration.registration_timestamp,
identity: gateway_details.gateway_id,
active: common_args.set_active,
typ: gateway_registration.details.typ().to_string(),
endpoint: Some(gateway_details.gateway_listener.clone()),
wg_tun_address: gateway_details.wg_tun_address.clone(),
})
}
@@ -0,0 +1,59 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli", clap(group(clap::ArgGroup::new("cred_data").required(true))))]
#[derive(Debug, Clone)]
pub struct CommonClientImportCredentialArgs {
/// Id of client that is going to import the credential
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Explicitly provide the encoded credential data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "cred_data", value_parser = parse_encoded_credential_data))]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[cfg_attr(feature = "cli", clap(long, group = "cred_data"))]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_credential<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportCredentialArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let raw_credential = match common_args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.credential_path.unwrap())?
}
};
nym_id::import_credential(credentials_store, raw_credential, common_args.version).await?;
Ok(())
}
@@ -1,27 +1,28 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::CommonClientPaths;
use crate::cli_helpers::traits::{CliClient, CliClientConfig};
use crate::error::ClientCoreError;
use crate::{
client::{
base_client::storage::gateway_details::OnDiskGatewayDetails,
base_client::{
non_wasm_helpers::setup_fs_gateways_storage, storage::helpers::set_active_gateway,
},
key_manager::persistence::OnDiskKeys,
},
init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup, InitResults},
init::types::{GatewaySelectionSpecification, GatewaySetup, InitResults},
};
use log::info;
use nym_client_core_gateways_storage::GatewayDetails;
use nym_crypto::asymmetric::identity;
use nym_topology::NymTopology;
use std::path::{Path, PathBuf};
use rand::rngs::OsRng;
use std::path::PathBuf;
pub trait InitialisableClient {
const NAME: &'static str;
type Error: From<ClientCoreError>;
// we can suppress this warning (as suggested by linter itself) since we're only using it in our own code
#[allow(async_fn_in_trait)]
pub trait InitialisableClient: CliClient {
type InitArgs: AsRef<CommonClientInitArgs>;
type Config: ClientConfig;
fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error>;
fn initialise_storage_paths(id: &str) -> Result<(), Self::Error>;
@@ -30,16 +31,6 @@ pub trait InitialisableClient {
fn construct_config(init_args: &Self::InitArgs) -> Self::Config;
}
pub trait ClientConfig {
fn common_paths(&self) -> &CommonClientPaths;
fn core_config(&self) -> &crate::config::Config;
fn default_store_location(&self) -> PathBuf;
fn save_to<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()>;
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[derive(Debug, Clone)]
pub struct CommonClientInitArgs {
@@ -51,16 +42,15 @@ pub struct CommonClientInitArgs {
#[cfg_attr(feature = "cli", clap(long))]
pub gateway: Option<identity::PublicKey>,
/// Specifies whether the client will attempt to enforce tls connection to the desired gateway.
#[cfg_attr(feature = "cli", clap(long))]
pub force_tls_gateway: bool,
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
/// uniformly.
#[cfg_attr(feature = "cli", clap(long, conflicts_with = "gateway"))]
pub latency_based_selection: bool,
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
/// potentially causing loss of access.
#[cfg_attr(feature = "cli", clap(long))]
pub force_register_gateway: bool,
/// Comma separated list of rest endpoints of the nyxd validators
#[cfg_attr(
feature = "cli",
@@ -109,7 +99,7 @@ pub async fn initialise_client<C>(
) -> Result<InitResultsWithConfig<C::Config>, C::Error>
where
C: InitialisableClient,
<C as InitialisableClient>::Config: std::fmt::Debug,
<C as CliClient>::Config: std::fmt::Debug,
<C as InitialisableClient>::InitArgs: std::fmt::Debug,
{
info!("initialising {} client", C::NAME);
@@ -117,28 +107,15 @@ where
let common_args = init_args.as_ref();
let id = &common_args.id;
let already_init = if C::default_config_path(id).exists() {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
C::try_upgrade_outdated_config(id)?;
if C::default_config_path(id).exists() {
eprintln!("{} client \"{id}\" was already initialised before", C::NAME);
true
} else {
C::initialise_storage_paths(id)?;
false
};
// Usually you only register with the gateway on the first init, however you can force
// re-registering if wanted.
let user_wants_force_register = common_args.force_register_gateway;
if user_wants_force_register {
eprintln!("Instructed to force registering gateway. This might overwrite keys!");
return Err(ClientCoreError::AlreadyInitialised {
client_id: id.to_string(),
}
.into());
}
// If the client was already initialized, don't generate new keys and don't re-register with
// the gateway (because this would create a new shared key).
// Unless the user really wants to.
let register_gateway = !already_init || user_wants_force_register;
C::initialise_storage_paths(id)?;
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = common_args.gateway;
@@ -147,7 +124,7 @@ where
let selection_spec = GatewaySelectionSpecification::new(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(common_args.latency_based_selection),
false,
common_args.force_tls_gateway,
);
log::debug!("Gateway selection specification: {selection_spec:?}");
@@ -168,11 +145,14 @@ where
.join(",")
);
let key_store = OnDiskKeys::new(paths.keys.clone());
let details_store = setup_fs_gateways_storage(&paths.gateway_registrations).await?;
let mut rng = OsRng;
crate::init::generate_new_client_keys(&mut rng, &key_store).await?;
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let key_store = OnDiskKeys::new(paths.keys.clone());
let details_store = OnDiskGatewayDetails::new(&paths.gateway_details);
let available_gateways = if let Some(custom_mixnet) = common_args.custom_mixnet.as_ref() {
let hardcoded_topology = NymTopology::new_from_file(custom_mixnet).map_err(|source| {
ClientCoreError::CustomTopologyLoadFailure {
@@ -189,14 +169,13 @@ where
let gateway_setup = GatewaySetup::New {
specification: selection_spec,
available_gateways,
overwrite_data: register_gateway,
wg_tun_address: None,
};
let init_details =
crate::init::setup_gateway(gateway_setup, &key_store, &details_store).await?;
// TODO: ask the service provider we specified for its interface version and set it in the config
let config_save_location = config.default_store_location();
if let Err(err) = config.save_to(&config_save_location) {
return Err(ClientCoreError::ConfigSaveFailure {
@@ -213,12 +192,20 @@ where
config_save_location.display()
);
let address = init_details.client_address()?;
let address = init_details.client_address();
let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else {
let GatewayDetails::Remote(gateway_details) = init_details.gateway_registration.details else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?;
};
let init_results = InitResults::new(config.core_config(), address, &gateway_details);
let init_results = InitResults::new(
config.core_config(),
address,
&gateway_details,
init_details.gateway_registration.registration_timestamp,
);
set_active_gateway(&details_store, &init_results.gateway_id).await?;
Ok(InitResultsWithConfig {
config,
@@ -0,0 +1,74 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::types::GatewayInfo;
use crate::cli_helpers::{CliClient, CliClientConfig};
use crate::client::base_client::non_wasm_helpers::setup_fs_gateways_storage;
use crate::client::base_client::storage::helpers::{
get_active_gateway_identity, get_gateway_registrations,
};
use nym_client_core_gateways_storage::{GatewayDetails, GatewayType};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[derive(Debug, Clone)]
pub struct CommonClientListGatewaysArgs {
/// Id of client we want to list gateways for.
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
}
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct RegisteredGateways(Vec<GatewayInfo>);
impl Display for RegisteredGateways {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for (i, gateway) in self.0.iter().enumerate() {
writeln!(f, "[{i}]: {gateway}")?;
}
Ok(())
}
}
pub async fn list_gateways<C, A>(args: A) -> Result<RegisteredGateways, C::Error>
where
A: AsRef<CommonClientListGatewaysArgs>,
C: CliClient,
{
let common_args = args.as_ref();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let details_store = setup_fs_gateways_storage(&paths.gateway_registrations).await?;
let active_gateway = get_active_gateway_identity(&details_store).await?;
let gateways = get_gateway_registrations(&details_store).await?;
let mut info = Vec::with_capacity(gateways.len());
for gateway in gateways {
match gateway.details {
GatewayDetails::Remote(remote_details) => info.push(GatewayInfo {
registration: gateway.registration_timestamp,
identity: remote_details.gateway_id,
active: active_gateway == Some(remote_details.gateway_id),
typ: GatewayType::Remote.to_string(),
endpoint: Some(remote_details.gateway_listener),
wg_tun_address: remote_details.wg_tun_address,
}),
GatewayDetails::Custom(_) => info.push(GatewayInfo {
registration: gateway.registration_timestamp,
identity: gateway.details.gateway_id(),
active: active_gateway == Some(gateway.details.gateway_id()),
typ: gateway.details.typ().to_string(),
endpoint: None,
wg_tun_address: None,
}),
};
}
Ok(RegisteredGateways(info))
}
@@ -0,0 +1,37 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use crate::client::base_client::non_wasm_helpers::setup_fs_gateways_storage;
use crate::client::base_client::storage::helpers::set_active_gateway;
use nym_crypto::asymmetric::identity;
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[derive(Debug, Clone)]
pub struct CommonClientSwitchGatewaysArgs {
/// Id of client we want to list gateways for.
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Id of the gateway we want to switch to.
#[cfg_attr(feature = "cli", clap(long))]
pub gateway_id: identity::PublicKey,
}
pub async fn switch_gateway<C, A>(args: A) -> Result<(), C::Error>
where
A: AsRef<CommonClientSwitchGatewaysArgs>,
C: CliClient,
{
let common_args = args.as_ref();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let details_store = setup_fs_gateways_storage(&paths.gateway_registrations).await?;
set_active_gateway(&details_store, &common_args.gateway_id.to_base58_string()).await?;
Ok(())
}
+10 -1
View File
@@ -1,5 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod client_add_gateway;
pub mod client_import_credential;
pub mod client_init;
pub mod client_list_gateways;
pub mod client_run;
pub mod client_switch_gateway;
pub mod traits;
mod types;
pub use client_init::InitialisableClient;
pub use traits::{CliClient, CliClientConfig};
@@ -0,0 +1,28 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::CommonClientPaths;
use crate::error::ClientCoreError;
use std::path::{Path, PathBuf};
// we can suppress this warning (as suggested by linter itself) since we're only using it in our own code
#[allow(async_fn_in_trait)]
pub trait CliClient {
const NAME: &'static str;
type Error: From<ClientCoreError>;
type Config: CliClientConfig;
async fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error>;
async fn try_load_current_config(id: &str) -> Result<Self::Config, Self::Error>;
}
pub trait CliClientConfig {
fn common_paths(&self) -> &CommonClientPaths;
fn core_config(&self) -> &crate::config::Config;
fn default_store_location(&self) -> PathBuf;
fn save_to<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()>;
}
@@ -0,0 +1,40 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_crypto::asymmetric::identity;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use time::OffsetDateTime;
use url::Url;
#[derive(Serialize, Deserialize)]
pub struct GatewayInfo {
pub registration: OffsetDateTime,
pub identity: identity::PublicKey,
pub active: bool,
pub typ: String,
pub endpoint: Option<Url>,
pub wg_tun_address: Option<Url>,
}
impl Display for GatewayInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if self.active {
write!(f, "[ACTIVE] ")?;
}
write!(
f,
"{} gateway '{}' registered at: {}",
self.typ, self.identity, self.registration
)?;
if let Some(endpoint) = &self.endpoint {
write!(f, " endpoint: {endpoint}")?;
}
if let Some(wg_tun_address) = &self.wg_tun_address {
write!(f, " wg tun address: {wg_tun_address}")?;
}
Ok(())
}
}
@@ -4,11 +4,12 @@
use super::packet_statistics_control::PacketStatisticsReporter;
use super::received_buffer::ReceivedBufferMessage;
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
use crate::client::base_client::storage::helpers::store_client_keys;
use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ClientKeys;
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::packet_statistics_control::PacketStatisticsControl;
@@ -30,17 +31,19 @@ use crate::config::{Config, DebugConfig};
use crate::error::ClientCoreError;
use crate::init::{
setup_gateway,
types::{GatewayDetails, GatewaySetup, InitialisationResult},
types::{GatewaySetup, InitialisationResult},
};
use crate::{config, spawn_future};
use futures::channel::mpsc;
use log::{debug, error, info};
use log::{debug, error, info, warn};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::encryption;
use nym_gateway_client::{
AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver, PacketRouter,
AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter,
};
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, WG_TUN_DEVICE_ADDRESS};
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity;
@@ -51,13 +54,18 @@ use nym_task::{TaskClient, TaskHandle};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use rand::rngs::OsRng;
use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
use std::path::Path;
use std::sync::Arc;
use url::Url;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
pub mod non_wasm_helpers;
pub mod helpers;
@@ -172,6 +180,7 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
dkg_query_client: Option<C>,
wait_for_gateway: bool,
wireguard_connection: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
shutdown: Option<TaskClient>,
@@ -194,10 +203,11 @@ where
client_store,
dkg_query_client,
wait_for_gateway: false,
wireguard_connection: false,
custom_topology_provider: None,
custom_gateway_transceiver: None,
shutdown: None,
setup_method: GatewaySetup::MustLoad,
setup_method: GatewaySetup::MustLoad { gateway_id: None },
}
}
@@ -213,6 +223,12 @@ where
self
}
#[must_use]
pub fn with_wireguard_connection(mut self, wireguard_connection: bool) -> Self {
self.wireguard_connection = wireguard_connection;
self
}
#[must_use]
pub fn with_topology_provider(
mut self,
@@ -246,13 +262,7 @@ where
// note: do **NOT** make this method public as its only valid usage is from within `start_base`
// because it relies on the crypto keys being already loaded
fn mix_address(details: &InitialisationResult) -> Recipient {
Recipient::new(
*details.managed_keys.identity_public_key(),
*details.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(details.gateway_details.gateway_id()).unwrap(),
)
details.client_address()
}
// future constantly pumping loop cover traffic at some specified average rate
@@ -342,6 +352,7 @@ where
async fn start_gateway_client(
config: &Config,
wireguard_connection: bool,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter,
@@ -351,50 +362,65 @@ where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
let managed_keys = initialisation_result.managed_keys;
let GatewayDetails::Configured(gateway_config) = initialisation_result.gateway_details
let managed_keys = initialisation_result.client_keys;
let GatewayDetails::Remote(details) = initialisation_result.gateway_registration.details
else {
return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails);
};
let mut gateway_client =
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client {
existing_client.upgrade(packet_router, bandwidth_controller, shutdown)
let mut gateway_client = if let Some(existing_client) =
initialisation_result.authenticated_ephemeral_client
{
existing_client.upgrade(packet_router, bandwidth_controller, shutdown)
} else {
let gateway_listener = if wireguard_connection {
if let Some(tun_address) = details.wg_tun_address {
tun_address.to_string()
} else {
let default =
format!("ws://{WG_TUN_DEVICE_ADDRESS}:{DEFAULT_CLIENT_LISTENING_PORT}");
warn!("gateway {} does not have tun device address set. defaulting to '{default}'", details.gateway_id);
default
}
} else {
let cfg = gateway_config.try_into()?;
GatewayClient::new(
cfg,
managed_keys.identity_keypair(),
Some(managed_keys.must_get_gateway_shared_key()),
packet_router,
bandwidth_controller,
shutdown,
)
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
.with_response_timeout(config.debug.gateway_connection.gateway_response_timeout)
details.gateway_listener.to_string()
};
let gateway_id = gateway_client.gateway_identity();
let cfg = GatewayConfig::new(
details.gateway_id,
Some(details.gateway_owner_address.to_string()),
gateway_listener,
);
GatewayClient::new(
cfg,
managed_keys.identity_keypair(),
Some(details.derived_aes128_ctr_blake3_hmac_keys),
packet_router,
bandwidth_controller,
shutdown,
)
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
.with_response_timeout(config.debug.gateway_connection.gateway_response_timeout)
};
let shared_key = gateway_client
gateway_client
.authenticate_and_start()
.await
.map_err(|err| {
log::error!("Could not authenticate and start up the gateway connection - {err}");
ClientCoreError::GatewayClientError {
gateway_id: gateway_id.to_base58_string(),
gateway_id: details.gateway_id.to_base58_string(),
source: err,
}
})?;
managed_keys.ensure_gateway_key(Some(shared_key));
Ok(gateway_client)
}
async fn setup_gateway_transceiver(
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
config: &Config,
wireguard_connection: bool,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
packet_router: PacketRouter,
@@ -406,7 +432,11 @@ where
{
// if we have setup custom gateway sender and persisted details agree with it, return it
if let Some(mut custom_gateway_transceiver) = custom_gateway_transceiver {
return if !initialisation_result.gateway_details.is_custom() {
return if !initialisation_result
.gateway_registration
.details
.is_custom()
{
Err(ClientCoreError::CustomGatewaySelectionExpected)
} else {
// and make sure to invalidate the task client so we wouldn't cause premature shutdown
@@ -419,6 +449,7 @@ where
// otherwise, setup normal gateway client, etc
let gateway_client = Self::start_gateway_client(
config,
wireguard_connection,
initialisation_result,
bandwidth_controller,
packet_router,
@@ -569,12 +600,20 @@ where
async fn initialise_keys_and_gateway(
setup_method: GatewaySetup,
key_store: &S::KeyStore,
details_store: &S::GatewayDetailsStore,
details_store: &S::GatewaysDetailsStore,
) -> Result<InitialisationResult, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
// if client keys do not exist already, create and persist them
if key_store.load_keys().await.is_err() {
info!("could not find valid client keys - a new set will be generated");
let mut rng = OsRng;
let keys = ClientKeys::generate_new(&mut rng);
store_client_keys(keys, key_store).await?;
}
setup_gateway(setup_method, key_store, details_store).await
}
@@ -584,7 +623,7 @@ where
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
info!("Starting nym client");
@@ -629,8 +668,8 @@ where
reply_controller::requests::new_control_channels();
let self_address = Self::mix_address(&init_res);
let ack_key = init_res.managed_keys.ack_key();
let encryption_keys = init_res.managed_keys.encryption_keypair();
let ack_key = init_res.client_keys.ack_key();
let encryption_keys = init_res.client_keys.encryption_keypair();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
@@ -667,6 +706,7 @@ where
let gateway_transceiver = Self::setup_gateway_transceiver(
self.custom_gateway_transceiver,
self.config,
self.wireguard_connection,
init_res,
bandwidth_controller,
gateway_packet_router,
@@ -1,4 +1,4 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::replies::reply_storage::{
@@ -7,8 +7,9 @@ use crate::client::replies::reply_storage::{
use crate::config;
use crate::config::Config;
use crate::error::ClientCoreError;
use log::{error, info};
use log::{error, info, trace};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_gateways_storage::OnDiskGatewaysDetails;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_validator_client::nyxd;
use nym_validator_client::QueryHttpRpcNyxdClient;
@@ -101,6 +102,17 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
}
}
pub async fn setup_fs_gateways_storage<P: AsRef<Path>>(
db_path: P,
) -> Result<OnDiskGatewaysDetails, ClientCoreError> {
trace!("setting up gateways details storage");
OnDiskGatewaysDetails::init(db_path)
.await
.map_err(|source| ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
})
}
pub fn create_bandwidth_controller<St: CredentialStorage>(
config: &Config,
storage: St,
@@ -1,296 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::types::{EmptyCustomDetails, GatewayDetails};
use async_trait::async_trait;
use log::error;
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;
use std::ops::Deref;
use tokio::sync::Mutex;
use zeroize::Zeroizing;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewayDetailsStore<T = EmptyCustomDetails> {
type StorageError: Error;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails<T>, Self::StorageError>
where
T: DeserializeOwned + Send + Sync;
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails<T>,
) -> Result<(), Self::StorageError>
where
T: Serialize + Send + Sync;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PersistedGatewayDetails<T = EmptyCustomDetails> {
/// Standard details of a remote gateway
Default(PersistedGatewayConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(PersistedCustomGatewayDetails<T>),
}
impl<T> PersistedGatewayDetails<T> {
// TODO: this should probably allow for custom verification over T
pub fn validate(&self, shared_key: Option<&SharedKeys>) -> Result<(), ClientCoreError> {
match self {
PersistedGatewayDetails::Default(details) => {
if !details.verify(shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
PersistedGatewayDetails::Custom(_) => {
if shared_key.is_some() {
error!("using custom persisted gateway setup with shared key present - are you sure that's what you want?");
// but technically we could still continue. just ignore the key
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PersistedGatewayConfig {
// TODO: should we also verify correctness of the details themselves?
// i.e. we could include a checksum or tag (via the shared keys)
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
// in order to actually use it, we'd have to recompute the whole checksum which would be a huge pain.
/// The hash of the shared keys to ensure the correct ones are used with those gateway details.
#[serde(with = "base64")]
key_hash: Vec<u8>,
/// Actual gateway details being persisted.
pub details: GatewayEndpointConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistedCustomGatewayDetails<T> {
// whatever custom method is used, gateway's identity must be known
pub gateway_id: String,
#[serde(flatten)]
pub additional_data: T,
}
impl PersistedGatewayConfig {
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize().to_vec();
PersistedGatewayConfig { key_hash, details }
}
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize();
self.key_hash == key_hash.deref()
}
}
impl<T> PersistedGatewayDetails<T> {
pub fn new(
details: GatewayDetails<T>,
shared_key: Option<&SharedKeys>,
) -> Result<Self, ClientCoreError> {
match details {
GatewayDetails::Configured(cfg) => {
let shared_key = shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?;
Ok(PersistedGatewayDetails::Default(
PersistedGatewayConfig::new(cfg, shared_key),
))
}
GatewayDetails::Custom(custom) => Ok(PersistedGatewayDetails::Custom(custom.into())),
}
}
pub fn is_custom(&self) -> bool {
matches!(self, PersistedGatewayDetails::Custom(..))
}
pub fn matches(&self, other: &GatewayDetails<T>) -> bool
where
T: PartialEq,
{
match self {
PersistedGatewayDetails::Default(default) => {
if let GatewayDetails::Configured(other_configured) = other {
&default.details == other_configured
} else {
false
}
}
PersistedGatewayDetails::Custom(custom) => {
if let GatewayDetails::Custom(other_custom) = other {
custom.gateway_id == other_custom.gateway_id
&& custom.additional_data == other_custom.additional_data
} else {
false
}
}
}
}
}
// helper to make Vec<u8> serialization use base64 representation to make it human readable
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
mod base64 {
use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&STANDARD.encode(bytes))
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
let s = <String>::deserialize(deserializer)?;
STANDARD.decode(s).map_err(serde::de::Error::custom)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, thiserror::Error)]
pub enum OnDiskGatewayDetailsError {
#[error("JSON failure: {0}")]
SerializationFailure(#[from] serde_json::Error),
#[error("failed to store gateway details to {path}: {err}")]
StoreFailure {
path: String,
#[source]
err: std::io::Error,
},
#[error("failed to load gateway details from {path}: {err}")]
LoadFailure {
path: String,
#[source]
err: std::io::Error,
},
}
#[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskGatewayDetails {
file_location: std::path::PathBuf,
}
#[cfg(not(target_arch = "wasm32"))]
impl OnDiskGatewayDetails {
pub fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
OnDiskGatewayDetails {
file_location: path.as_ref().to_owned(),
}
}
pub fn load_from_disk<T>(&self) -> Result<PersistedGatewayDetails<T>, OnDiskGatewayDetailsError>
where
T: DeserializeOwned,
{
let file = std::fs::File::open(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::LoadFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::from_reader(file)?)
}
pub fn store_to_disk<T>(
&self,
details: &PersistedGatewayDetails<T>,
) -> Result<(), OnDiskGatewayDetailsError>
where
T: Serialize,
{
// ensure the whole directory structure exists
if let Some(parent_dir) = &self.file_location.parent() {
std::fs::create_dir_all(parent_dir).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?
}
let file = std::fs::File::create(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::to_writer_pretty(file, details)?)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for OnDiskGatewayDetails {
type StorageError = OnDiskGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.load_from_disk()
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
self.store_to_disk(gateway_details)
}
}
#[derive(Default)]
pub struct InMemGatewayDetails<T = EmptyCustomDetails> {
details: Mutex<Option<PersistedGatewayDetails<T>>>,
}
#[derive(Debug, thiserror::Error)]
#[error("old ephemeral gateway details can't be loaded from storage")]
pub struct EphemeralGatewayDetailsError;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for InMemGatewayDetails {
type StorageError = EphemeralGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.details
.lock()
.await
.clone()
.ok_or(EphemeralGatewayDetailsError)
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
*self.details.lock().await = Some(gateway_details.clone());
Ok(())
}
}
@@ -0,0 +1,157 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ClientKeys;
use crate::error::ClientCoreError;
use nym_client_core_gateways_storage::{ActiveGateway, GatewayRegistration, GatewaysDetailsStore};
use nym_crypto::asymmetric::identity;
// helpers for error wrapping
pub async fn set_active_gateway<D>(
details_store: &D,
gateway_id: &str,
) -> Result<(), ClientCoreError>
where
D: GatewaysDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store
.set_active_gateway(gateway_id)
.await
.map_err(|source| ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
})
}
pub async fn get_active_gateway_identity<D>(
details_store: &D,
) -> Result<Option<identity::PublicKey>, ClientCoreError>
where
D: GatewaysDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store
.active_gateway()
.await
.map_err(|source| ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
})
.map(|a| a.registration.map(|r| r.details.gateway_id()))
}
pub async fn get_all_registered_identities<D>(
details_store: &D,
) -> Result<Vec<identity::PublicKey>, ClientCoreError>
where
D: GatewaysDetailsStore + Sync,
D::StorageError: Send + Sync + 'static,
{
details_store
.all_gateways_identities()
.await
.map_err(|source| ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
})
}
pub async fn get_gateway_registrations<D>(
details_store: &D,
) -> Result<Vec<GatewayRegistration>, ClientCoreError>
where
D: GatewaysDetailsStore + Sync,
D::StorageError: Send + Sync + 'static,
{
details_store.all_gateways().await.map_err(|source| {
ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
}
})
}
pub async fn store_gateway_details<D>(
details_store: &D,
details: &GatewayRegistration,
) -> Result<(), ClientCoreError>
where
D: GatewaysDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store
.store_gateway_details(details)
.await
.map_err(|source| ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
})
}
pub async fn load_active_gateway_details<D>(
details_store: &D,
) -> Result<ActiveGateway, ClientCoreError>
where
D: GatewaysDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store.active_gateway().await.map_err(|source| {
ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
}
})
}
pub async fn load_gateway_details<D>(
details_store: &D,
gateway_id: &str,
) -> Result<GatewayRegistration, ClientCoreError>
where
D: GatewaysDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store
.load_gateway_details(gateway_id)
.await
.map_err(|source| ClientCoreError::UnavailableGatewayDetails {
gateway_id: gateway_id.to_string(),
source: Box::new(source),
})
}
pub async fn has_gateway_details<D>(
details_store: &D,
gateway_id: &str,
) -> Result<bool, ClientCoreError>
where
D: GatewaysDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store
.has_gateway_details(gateway_id)
.await
.map_err(|source| ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
})
}
pub async fn load_client_keys<K>(key_store: &K) -> Result<ClientKeys, ClientCoreError>
where
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
ClientKeys::load_keys(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})
}
pub async fn store_client_keys<K>(keys: ClientKeys, key_store: &K) -> Result<(), ClientCoreError>
where
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
keys.persist_keys(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})
}
@@ -0,0 +1,212 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod v1_1_33 {
use crate::client::base_client::{
non_wasm_helpers::setup_fs_gateways_storage,
storage::helpers::{set_active_gateway, store_gateway_details},
};
use crate::config::disk_persistence::old_v1_1_33::CommonClientPathsV1_1_33;
use crate::config::disk_persistence::CommonClientPaths;
use crate::config::old_config_v1_1_33::OldGatewayEndpointConfigV1_1_33;
use crate::error::ClientCoreError;
use nym_client_core_gateways_storage::{
CustomGatewayDetails, GatewayDetails, GatewayRegistration, RemoteGatewayDetails,
};
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::{Deserialize, Serialize};
use sha2::{digest::Digest, Sha256};
use std::ops::Deref;
use std::path::Path;
use std::sync::Arc;
use zeroize::Zeroizing;
mod base64 {
use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&STANDARD.encode(bytes))
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<u8>, D::Error> {
let s = <String>::deserialize(deserializer)?;
STANDARD.decode(s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
enum PersistedGatewayDetails {
/// Standard details of a remote gateway
Default(PersistedGatewayConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(PersistedCustomGatewayDetails),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct PersistedGatewayConfig {
/// The hash of the shared keys to ensure the correct ones are used with those gateway details.
#[serde(with = "base64")]
key_hash: Vec<u8>,
/// Actual gateway details being persisted.
details: OldGatewayEndpointConfigV1_1_33,
}
impl PersistedGatewayConfig {
fn verify(&self, shared_key: &SharedKeys) -> bool {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize();
self.key_hash == key_hash.deref()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct PersistedCustomGatewayDetails {
gateway_id: String,
}
fn load_shared_key<P: AsRef<Path>>(path: P) -> Result<SharedKeys, ClientCoreError> {
// the shared key was a simple pem file
Ok(nym_pemstore::load_key(path)?)
}
fn gateway_details_from_raw(
gateway_id: String,
gateway_owner: String,
gateway_listener: String,
gateway_shared_key: SharedKeys,
) -> Result<GatewayDetails, ClientCoreError> {
Ok(GatewayDetails::Remote(RemoteGatewayDetails {
gateway_id: gateway_id
.parse()
.map_err(|err| ClientCoreError::UpgradeFailure {
message: format!("the stored gateway id was malformed: {err}"),
})?,
derived_aes128_ctr_blake3_hmac_keys: Arc::new(gateway_shared_key),
gateway_owner_address: gateway_owner.parse().map_err(|err| {
ClientCoreError::UpgradeFailure {
message: format!("the stored gateway owner address was malformed: {err}"),
}
})?,
gateway_listener: gateway_listener.parse().map_err(|err| {
ClientCoreError::UpgradeFailure {
message: format!("the stored gateway listener address was malformed: {err}"),
}
})?,
wg_tun_address: None,
}))
}
// helper to extract shared key and gateway details into the new GatewayRegistration
fn extract_gateway_registration(
storage_paths: &CommonClientPathsV1_1_33,
) -> Result<GatewayRegistration, ClientCoreError> {
let details_file = std::fs::File::open(&storage_paths.gateway_details).map_err(|err| {
ClientCoreError::UpgradeFailure {
message: format!(
"failed to open gateway details file at {}: {err}",
storage_paths.gateway_details.display()
),
}
})?;
// in v1.1.33 of the clients, the gateway details struct was saved as json
let details: PersistedGatewayDetails =
serde_json::from_reader(details_file).map_err(|err| {
ClientCoreError::UpgradeFailure {
message: format!(
"failed to deserialize gateway details from {}: {err}",
storage_paths.gateway_details.display()
),
}
})?;
let details = match details {
PersistedGatewayDetails::Default(config) => {
let gateway_shared_key =
load_shared_key(&storage_paths.keys.gateway_shared_key_file)?;
if !config.verify(&gateway_shared_key) {
return Err(ClientCoreError::UpgradeFailure {
message: "failed to verify consistency of the existing gateway details"
.to_string(),
});
}
gateway_details_from_raw(
config.details.gateway_id,
config.details.gateway_owner,
config.details.gateway_listener,
gateway_shared_key,
)?
}
PersistedGatewayDetails::Custom(custom) => {
GatewayDetails::Custom(CustomGatewayDetails {
gateway_id: custom.gateway_id.parse().map_err(|err| {
ClientCoreError::UpgradeFailure {
message: format!("the stored gateway id was malformed: {err}"),
}
})?,
data: None,
})
}
};
Ok(details.into())
}
// it's responsibility of the caller to ensure this is called **after** new registration has already been saved
fn remove_old_gateway_details(storage_paths: &CommonClientPathsV1_1_33) -> std::io::Result<()> {
std::fs::remove_file(&storage_paths.gateway_details)?;
if storage_paths.keys.gateway_shared_key_file.exists() {
std::fs::remove_file(&storage_paths.keys.gateway_shared_key_file)?;
}
Ok(())
}
pub async fn migrate_gateway_details(
old_storage_paths: &CommonClientPathsV1_1_33,
new_storage_paths: &CommonClientPaths,
preloaded_config: Option<OldGatewayEndpointConfigV1_1_33>,
) -> Result<(), ClientCoreError> {
let gateway_registration = match preloaded_config {
Some(config) => {
let gateway_shared_key =
load_shared_key(&old_storage_paths.keys.gateway_shared_key_file)?;
gateway_details_from_raw(
config.gateway_id,
config.gateway_owner,
config.gateway_listener,
gateway_shared_key,
)?
.into()
}
None => extract_gateway_registration(old_storage_paths)?,
};
// since we're migrating to a brand new store, the store should be empty
// and thus set the 'new' gateway as the active one
let details_store =
setup_fs_gateways_storage(&new_storage_paths.gateway_registrations).await?;
store_gateway_details(&details_store, &gateway_registration).await?;
set_active_gateway(
&details_store,
&gateway_registration.details.gateway_id().to_base58_string(),
)
.await?;
remove_old_gateway_details(old_storage_paths).map_err(|err| {
ClientCoreError::UpgradeFailure {
message: format!("failed to remove old data: {err}"),
}
})
}
}
@@ -4,46 +4,57 @@
// TODO: combine those more closely. Perhaps into a single underlying store.
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, InMemGatewayDetails,
};
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
use crate::client::replies::reply_storage;
use crate::client::replies::reply_storage::ReplyStorageBackend;
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use nym_credential_storage::storage::Storage as CredentialStorage;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::non_wasm_helpers;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::key_manager::persistence::OnDiskKeys;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::replies::reply_storage::fs_backend;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::config::{self, disk_persistence::CommonClientPaths};
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::error::ClientCoreError;
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
use crate::{
client::{
base_client::non_wasm_helpers, key_manager::persistence::OnDiskKeys,
replies::reply_storage::fs_backend,
},
config::{self, disk_persistence::CommonClientPaths},
error::ClientCoreError,
};
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
pub mod gateway_details;
pub use nym_client_core_gateways_storage as gateways_storage;
pub use nym_client_core_gateways_storage::{GatewaysDetailsStore, InMemGatewaysDetails};
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-gateways-storage"))]
pub use nym_client_core_gateways_storage::{OnDiskGatewaysDetails, StorageError};
pub mod helpers;
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
pub mod migration_helpers;
// TODO: ideally this should be changed into
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewayDetailsStore`
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewaysDetailsStore`
pub trait MixnetClientStorage {
type KeyStore: KeyStore;
type ReplyStore: ReplyStorageBackend;
type CredentialStore: CredentialStorage;
type GatewayDetailsStore: GatewayDetailsStore;
type GatewaysDetailsStore: GatewaysDetailsStore;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
fn key_store(&self) -> &Self::KeyStore;
fn reply_store(&self) -> &Self::ReplyStore;
fn credential_store(&self) -> &Self::CredentialStore;
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore;
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
}
#[derive(Default)]
@@ -51,7 +62,7 @@ pub struct Ephemeral {
key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty,
credential_store: EphemeralCredentialStorage,
gateway_details_store: InMemGatewayDetails,
gateway_details_store: InMemGatewaysDetails,
}
impl Ephemeral {
@@ -64,7 +75,7 @@ impl MixnetClientStorage for Ephemeral {
type KeyStore = InMemEphemeralKeys;
type ReplyStore = reply_storage::Empty;
type CredentialStore = EphemeralCredentialStorage;
type GatewayDetailsStore = InMemGatewayDetails;
type GatewaysDetailsStore = InMemGatewaysDetails;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store)
@@ -82,26 +93,34 @@ impl MixnetClientStorage for Ephemeral {
&self.credential_store
}
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore {
&self.gateway_details_store
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
pub struct OnDiskPersistent {
pub(crate) key_store: OnDiskKeys,
pub(crate) reply_store: fs_backend::Backend,
pub(crate) credential_store: PersistentCredentialStorage,
pub(crate) gateway_details_store: OnDiskGatewayDetails,
pub(crate) gateway_details_store: OnDiskGatewaysDetails,
}
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
impl OnDiskPersistent {
pub fn new(
key_store: OnDiskKeys,
reply_store: fs_backend::Backend,
credential_store: PersistentCredentialStorage,
gateway_details_store: OnDiskGatewayDetails,
gateway_details_store: OnDiskGatewaysDetails,
) -> Self {
Self {
key_store,
@@ -126,7 +145,8 @@ impl OnDiskPersistent {
let credential_store =
nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await;
let gateway_details_store = OnDiskGatewayDetails::new(paths.gateway_details);
let gateway_details_store =
non_wasm_helpers::setup_fs_gateways_storage(paths.gateway_registrations).await?;
Ok(OnDiskPersistent {
key_store,
@@ -137,12 +157,16 @@ impl OnDiskPersistent {
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
impl MixnetClientStorage for OnDiskPersistent {
type KeyStore = OnDiskKeys;
type ReplyStore = fs_backend::Backend;
type CredentialStore = PersistentCredentialStorage;
type GatewayDetailsStore = OnDiskGatewayDetails;
type GatewaysDetailsStore = OnDiskGatewaysDetails;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store)
@@ -160,7 +184,7 @@ impl MixnetClientStorage for OnDiskPersistent {
&self.credential_store
}
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore {
&self.gateway_details_store
}
}
+14 -251
View File
@@ -6,225 +6,11 @@ use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore};
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use zeroize::ZeroizeOnDrop;
pub mod persistence;
pub enum ManagedKeys {
Initial(KeyManagerBuilder),
FullyDerived(KeyManager),
// I really hate the existence of this variant, but I couldn't come up with a better way to handle
// `Self::deal_with_gateway_key` otherwise.
Invalidated,
}
impl Debug for ManagedKeys {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ManagedKeys::Initial(_) => write!(f, "initial"),
ManagedKeys::FullyDerived(_) => write!(f, "fully derived"),
ManagedKeys::Invalidated => write!(f, "invalidated"),
}
}
}
impl From<KeyManagerBuilder> for ManagedKeys {
fn from(value: KeyManagerBuilder) -> Self {
ManagedKeys::Initial(value)
}
}
impl From<KeyManager> for ManagedKeys {
fn from(value: KeyManager) -> Self {
ManagedKeys::FullyDerived(value)
}
}
impl ManagedKeys {
pub fn is_valid(&self) -> bool {
!matches!(self, ManagedKeys::Invalidated)
}
pub async fn try_load<S: KeyStore>(key_store: &S) -> Result<Self, S::StorageError> {
Ok(ManagedKeys::FullyDerived(
KeyManager::load_keys(key_store).await?,
))
}
pub fn generate_new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
ManagedKeys::Initial(KeyManagerBuilder::new(rng))
}
pub async fn load_or_generate<R, S>(rng: &mut R, key_store: &S) -> Self
where
R: RngCore + CryptoRng,
S: KeyStore,
{
Self::try_load(key_store)
.await
.unwrap_or_else(|_| Self::generate_new(rng))
}
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
match self {
ManagedKeys::Initial(keys) => keys.identity_keypair(),
ManagedKeys::FullyDerived(keys) => keys.identity_keypair(),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
match self {
ManagedKeys::Initial(keys) => keys.encryption_keypair(),
ManagedKeys::FullyDerived(keys) => keys.encryption_keypair(),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
pub fn ack_key(&self) -> Arc<AckKey> {
match self {
ManagedKeys::Initial(keys) => keys.ack_key(),
ManagedKeys::FullyDerived(keys) => keys.ack_key(),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
pub fn must_get_gateway_shared_key(&self) -> Arc<SharedKeys> {
self.gateway_shared_key()
.expect("failed to extract gateway shared key")
}
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
match self {
ManagedKeys::Initial(_) => None,
ManagedKeys::FullyDerived(keys) => keys.gateway_shared_key(),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
pub fn identity_public_key(&self) -> &identity::PublicKey {
match self {
ManagedKeys::Initial(keys) => keys.identity_keypair.public_key(),
ManagedKeys::FullyDerived(keys) => keys.identity_keypair.public_key(),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
pub fn encryption_public_key(&self) -> &encryption::PublicKey {
match self {
ManagedKeys::Initial(keys) => keys.encryption_keypair.public_key(),
ManagedKeys::FullyDerived(keys) => keys.encryption_keypair.public_key(),
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
}
}
pub fn ensure_gateway_key(&self, gateway_shared_key: Option<Arc<SharedKeys>>) {
if let ManagedKeys::FullyDerived(key_manager) = &self {
if self.gateway_shared_key().is_none() && gateway_shared_key.is_none() {
// the key doesn't exist in either state
return;
}
if gateway_shared_key.is_some() && self.gateway_shared_key().is_none()
|| gateway_shared_key.is_none() && self.gateway_shared_key().is_some()
{
// if one is provided whilst the other is not...
// TODO: should this actually panic or return an error? would this branch be possible
// under normal operation?
panic!("inconsistent re-derived gateway key")
}
// here we know both keys MUST exist
let provided = gateway_shared_key.unwrap();
if !Arc::ptr_eq(key_manager.must_get_gateway_shared_key(), &provided)
|| *key_manager.must_get_gateway_shared_key() != provided
{
// this should NEVER happen thus panic here
panic!("derived fresh gateway shared key whilst already holding one!")
}
}
}
pub async fn deal_with_gateway_key<S: KeyStore>(
&mut self,
gateway_shared_key: Option<Arc<SharedKeys>>,
key_store: &S,
) -> Result<(), S::StorageError> {
let key_manager = match std::mem::replace(self, ManagedKeys::Invalidated) {
ManagedKeys::Initial(keys) => {
let key_manager = keys.insert_maybe_gateway_shared_key(gateway_shared_key);
key_manager.persist_keys(key_store).await?;
key_manager
}
ManagedKeys::FullyDerived(key_manager) => {
self.ensure_gateway_key(gateway_shared_key);
key_manager
}
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
};
*self = ManagedKeys::FullyDerived(key_manager);
Ok(())
}
}
// all of the keys really shouldn't be wrapped in `Arc`, but due to how the gateway client is currently
// constructed, changing that would require more work than what it's worth
pub struct KeyManagerBuilder {
/// identity key associated with the client instance.
identity_keypair: Arc<identity::KeyPair>,
/// encryption key associated with the client instance.
encryption_keypair: Arc<encryption::KeyPair>,
/// key used for producing and processing acknowledgement packets.
ack_key: Arc<AckKey>,
}
impl KeyManagerBuilder {
/// Creates new instance of a [`KeyManager`]
pub fn new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
KeyManagerBuilder {
identity_keypair: Arc::new(identity::KeyPair::new(rng)),
encryption_keypair: Arc::new(encryption::KeyPair::new(rng)),
ack_key: Arc::new(AckKey::new(rng)),
}
}
pub fn insert_maybe_gateway_shared_key(
self,
gateway_shared_key: Option<Arc<SharedKeys>>,
) -> KeyManager {
KeyManager {
identity_keypair: self.identity_keypair,
encryption_keypair: self.encryption_keypair,
gateway_shared_key,
ack_key: self.ack_key,
}
}
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
Arc::clone(&self.identity_keypair)
}
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
Arc::clone(&self.encryption_keypair)
}
pub fn ack_key(&self) -> Arc<AckKey> {
Arc::clone(&self.ack_key)
}
}
// Note: to support key rotation in the future, all keys will require adding an extra smart pointer,
// most likely an AtomicCell, or if it doesn't work as I think it does, a Mutex. Although I think
// AtomicCell includes a Mutex implicitly if the underlying type does not work atomically.
@@ -233,35 +19,38 @@ impl KeyManagerBuilder {
// Remember that Arc<T> has Deref implementation for T
#[derive(Clone)]
pub struct KeyManager {
pub struct ClientKeys {
/// identity key associated with the client instance.
identity_keypair: Arc<identity::KeyPair>,
/// encryption key associated with the client instance.
encryption_keypair: Arc<encryption::KeyPair>,
/// shared key derived with the gateway during "registration handshake"
// I'm not a fan of how we broke the nice transition of `KeyManagerBuilder` -> `KeyManager`
// by making this field optional.
// However, it has to be optional for when we use embedded NR inside a gateway,
// since it won't have a shared key (because why would it?)
gateway_shared_key: Option<Arc<SharedKeys>>,
/// key used for producing and processing acknowledgement packets.
ack_key: Arc<AckKey>,
}
impl KeyManager {
impl ClientKeys {
/// Creates new instance of a [`ClientKeys`]
pub fn generate_new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
ClientKeys {
identity_keypair: Arc::new(identity::KeyPair::new(rng)),
encryption_keypair: Arc::new(encryption::KeyPair::new(rng)),
ack_key: Arc::new(AckKey::new(rng)),
}
}
pub fn from_keys(
id_keypair: identity::KeyPair,
enc_keypair: encryption::KeyPair,
gateway_shared_key: Option<SharedKeys>,
ack_key: AckKey,
) -> Self {
Self {
identity_keypair: Arc::new(id_keypair),
encryption_keypair: Arc::new(enc_keypair),
gateway_shared_key: gateway_shared_key.map(Arc::new),
ack_key: Arc::new(ack_key),
}
}
@@ -287,32 +76,6 @@ impl KeyManager {
pub fn ack_key(&self) -> Arc<AckKey> {
Arc::clone(&self.ack_key)
}
fn must_get_gateway_shared_key(&self) -> &Arc<SharedKeys> {
self.gateway_shared_key
.as_ref()
.expect("gateway shared key is unavailable")
}
pub fn uses_custom_gateway(&self) -> bool {
self.gateway_shared_key.is_none()
}
/// Gets an atomically reference counted pointer to [`SharedKey`].
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
self.gateway_shared_key.as_ref().map(Arc::clone)
}
pub fn remove_gateway_key(self) -> KeyManagerBuilder {
if Arc::strong_count(self.must_get_gateway_shared_key()) > 1 {
panic!("attempted to remove gateway key whilst still holding multiple references!")
}
KeyManagerBuilder {
identity_keypair: self.identity_keypair,
encryption_keypair: self.encryption_keypair,
ack_key: self.ack_key,
}
}
}
fn _assert_keys_zeroize_on_drop() {
@@ -1,18 +1,16 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::key_manager::KeyManager;
use crate::client::key_manager::ClientKeys;
use async_trait::async_trait;
use std::error::Error;
use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))]
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
use crate::config::disk_persistence::ClientKeysPaths;
#[cfg(not(target_arch = "wasm32"))]
use nym_crypto::asymmetric::{encryption, identity};
#[cfg(not(target_arch = "wasm32"))]
use nym_gateway_requests::registration::handshake::SharedKeys;
#[cfg(not(target_arch = "wasm32"))]
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
#[cfg(not(target_arch = "wasm32"))]
use nym_pemstore::KeyPairPath;
@@ -25,9 +23,9 @@ use nym_sphinx::acknowledgements::AckKey;
pub trait KeyStore {
type StorageError: Error;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError>;
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError>;
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError>;
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError>;
}
#[cfg(not(target_arch = "wasm32"))]
@@ -84,14 +82,6 @@ impl OnDiskKeys {
OnDiskKeys { paths }
}
#[doc(hidden)]
pub fn ephemeral_load_gateway_keys(
&self,
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
self.load_key(self.paths.gateway_shared_key(), "gateway shared")
.map(zeroize::Zeroizing::new)
}
#[doc(hidden)]
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
let encryption_paths = self.paths.encryption_key_pair_path();
@@ -156,26 +146,19 @@ impl OnDiskKeys {
})
}
fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> {
fn load_keys(&self) -> Result<ClientKeys, OnDiskKeysError> {
let identity_keypair = self.load_identity_keypair()?;
let encryption_keypair = self.load_encryption_keypair()?;
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
let gateway_shared_key: Option<SharedKeys> = self
.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
.ok();
Ok(KeyManager::from_keys(
Ok(ClientKeys::from_keys(
identity_keypair,
encryption_keypair,
gateway_shared_key,
ack_key,
))
}
fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> {
use std::ops::Deref;
fn store_keys(&self, keys: &ClientKeys) -> Result<(), OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path();
@@ -192,14 +175,6 @@ impl OnDiskKeys {
self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?;
if let Some(shared_keys) = &keys.gateway_shared_key {
self.store_key(
shared_keys.deref(),
self.paths.gateway_shared_key(),
"gateway shared keys",
)?;
}
Ok(())
}
}
@@ -209,18 +184,18 @@ impl OnDiskKeys {
impl KeyStore for OnDiskKeys {
type StorageError = OnDiskKeysError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
self.load_keys()
}
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
self.store_keys(keys)
}
}
#[derive(Default)]
pub struct InMemEphemeralKeys {
keys: Mutex<Option<KeyManager>>,
keys: Mutex<Option<ClientKeys>>,
}
#[derive(Debug, thiserror::Error)]
@@ -232,11 +207,11 @@ pub struct EphemeralKeysError;
impl KeyStore for InMemEphemeralKeys {
type StorageError = EphemeralKeysError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
}
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
*self.keys.lock().await = Some(keys.clone());
Ok(())
}
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
use crate::error::ClientCoreError;
use crate::spawn_future;
use log::*;
use nym_sphinx::forwarding::packet::MixPacket;
@@ -60,8 +61,15 @@ impl MixTrafficController {
)
}
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
async fn on_messages(
&mut self,
mut mix_packets: Vec<MixPacket>,
) -> Result<(), ClientCoreError> {
debug_assert!(!mix_packets.is_empty());
info!(
"JON: MixTrafficController: Sending {} sphinx packets to the gateway",
mix_packets.len()
);
let result = if mix_packets.len() == 1 {
let mix_packet = mix_packets.pop().unwrap();
@@ -72,42 +80,56 @@ impl MixTrafficController {
.await
};
match result {
let r = match result {
Err(err) => {
error!("Failed to send sphinx packet(s) to the gateway: {err}");
self.consecutive_gateway_failure_count += 1;
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// todo: in the future this should initiate a 'graceful' shutdown or try
// to reconnect?
panic!("failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead. Can't do anything about it yet :(")
Err(ClientCoreError::GatewayMaxRetriesExceeded)
} else {
Err(ClientCoreError::GatewayClientSendError {
gateway_client_error: err.to_string(),
})
}
}
Ok(_) => {
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
self.consecutive_gateway_failure_count = 0;
Ok(())
}
}
};
info!("JON: MixTrafficController: done sending sphinx packets to the gateway");
r
}
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move {
debug!("Started MixTrafficController with graceful shutdown support");
// let mut shutdown0 = shutdown.recv_with_delay();
// tokio::pin!(shutdown0);
loop {
tokio::select! {
biased;
_ = shutdown.recv_with_delay() => {
// _ = &mut shutdown0 => {
log::trace!("MixTrafficController: Received shutdown");
break;
}
mix_packets = self.mix_rx.recv() => match mix_packets {
Some(mix_packets) => {
self.on_messages(mix_packets).await;
log::info!("JON: MixTrafficController: mix_rx recv");
if let Err(err) = self.on_messages(mix_packets).await {
log::error!("MixTrafficController: failed to send mix packets to the gateway: {err}");
}
log::info!("JON: MixTrafficController: done with mix_rx recv");
},
None => {
log::trace!("MixTrafficController: Stopping since channel closed");
break;
}
},
_ = shutdown.recv_with_delay() => {
log::trace!("MixTrafficController: Received shutdown");
break;
}
}
}
shutdown.recv_timeout().await;
@@ -19,6 +19,12 @@ use futures::channel::{mpsc, oneshot};
#[error(transparent)]
pub struct ErasedGatewayError(Box<dyn std::error::Error + Send + Sync>);
impl ErasedGatewayError {
pub fn downcast<T: std::error::Error + 'static>(&self) -> Option<&T> {
self.0.downcast_ref::<T>()
}
}
fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGatewayError {
ErasedGatewayError(Box::new(err))
}
@@ -40,6 +46,7 @@ pub trait GatewaySender {
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
log::info!("GatewaySender::batch_send_mix_packets - sending {} packets", packets.len());
// allow for optimisation when sending multiple packets
for packet in packets {
self.send_mix_packet(packet).await?;
@@ -78,7 +85,10 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
impl<G: GatewaySender + ?Sized + Send> GatewaySender for Box<G> {
#[inline]
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
(**self).send_mix_packet(packet).await
log::info!("JON: Box<GatewaySender>::send_mix_packet - sending a packet");
let r = (**self).send_mix_packet(packet).await;
log::info!("JON: Box<GatewaySender>::send_mix_packet - sent a packet");
r
}
#[inline]
@@ -86,7 +96,10 @@ impl<G: GatewaySender + ?Sized + Send> GatewaySender for Box<G> {
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
(**self).batch_send_mix_packets(packets).await
log::info!("JON: Box<GatewaySender>::batch_send_mix_packets - sending {} packets", packets.len());
let r = (**self).batch_send_mix_packets(packets).await;
log::info!("JON: Box<GatewaySender>::batch_send_mix_packets - sent packets");
r
}
}
@@ -130,20 +143,26 @@ where
St: Send,
{
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.gateway_client
log::info!("JON: RemoteGateway::send_mix_packet - sending a packet");
let r = self.gateway_client
.send_mix_packet(packet)
.await
.map_err(erase_err)
.map_err(erase_err);
log::info!("JON: RemoteGateway::send_mix_packet - sent a packet");
r
}
async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
self.gateway_client
log::info!("JON: RemoteGateway::batch_send_mix_packets - sending {} packets", packets.len());
let r = self.gateway_client
.batch_send_mix_packets(packets)
.await
.map_err(erase_err)
.map_err(erase_err);
log::info!("JON: RemoteGateway::batch_send_mix_packets - sent packets");
r
}
}
@@ -203,10 +222,13 @@ mod nonwasm_sealed {
#[async_trait]
impl GatewaySender for LocalGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.packet_forwarder
log::info!("JON: LocalGateway::send_mix_packet - sending a packet");
let r = self.packet_forwarder
.unbounded_send(packet)
.map_err(|err| err.into_send_error())
.map_err(erase_err)
.map_err(erase_err);
log::info!("JON: LocalGateway::send_mix_packet - sent a packet");
r
}
}
@@ -261,6 +283,7 @@ impl GatewayReceiver for MockGateway {
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewaySender for MockGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
log::info!("MockGateway::send_mix_packet - sending a packet");
self.sent.push(packet);
Ok(())
}

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