Compare commits

..

246 Commits

Author SHA1 Message Date
Tommy Verrall 4621978a54 add models 2024-05-02 14:04:28 +02:00
Tommy Verrall 93a1857c6b make models public 2024-05-02 13:41:07 +02:00
Tommy Verrall c6942769fc more more more 2024-05-02 13:24:40 +02:00
Tommy Verrall 33118ad648 a lot more overhead 2024-05-02 13:09:29 +02:00
Tommy Verrall 1cd489034f pr comments 2024-05-02 11:57:47 +02:00
Tommy Verrall 6c731a2f06 round and round with error logging 2024-05-02 11:56:24 +02:00
Tommy Verrall 5dda372437 use anyhow instead to return error messages 2024-05-02 11:26:01 +02:00
Tommy Verrall edf9c0f7b5 simplify the error logging 2024-05-02 11:19:26 +02:00
Tommy Verrall c371e6c4bc cargo fmt 2024-05-02 11:12:12 +02:00
Tommy Verrall 4198cd81f3 including proper reqwest error message 2024-05-02 11:09:50 +02:00
Tommy Verrall bbf57482fc change stats model to cater for both endpoints after nym-node intro 2024-05-02 11:01:50 +02:00
Tommy Verrall e59a444074 include nym-node endpoint 2024-05-02 10:25:20 +02:00
Tommy Verrall f99bedd7e8 Merge pull request #4566 from nymtech/dependabot/npm_and_yarn/nym-wallet/webdriver/ejs-3.1.10
Bump ejs from 3.1.7 to 3.1.10 in /nym-wallet/webdriver
2024-05-02 08:01:12 +01:00
dependabot[bot] 7717bf5cf9 Bump ejs from 3.1.7 to 3.1.10 in /nym-wallet/webdriver
Bumps [ejs](https://github.com/mde/ejs) from 3.1.7 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.7...v3.1.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-02 00:47:12 +00:00
import this f34c9d5d28 [DOCs]: Get nym-vpn guides ready for launch (#4563)
* update nymvpn cli guide

* update nymvpn landing page

* update nymvpn landing page

* syntax edit

* final version for review

* final version for review

* fix on feedback

* fix on feedback
2024-04-29 15:07:51 +00:00
import this b93afe7756 add useful feedback and edits (#4562) 2024-04-26 17:22:24 +00:00
benedetta davico 140cd7d940 Merge pull request #4560 from nymtech/master
Master to develop
2024-04-25 12:59:58 +02:00
benedetta davico 7d233a4a2f Merge pull request #4559 from nymtech/release/2024.3-eclipse
Merge release/2024.3 eclipse to master
2024-04-25 11:27:18 +02:00
import this 5f60344c2b HOTFIX ci-docs (#4558)
* HOTFIX ci-docs

* add a flag to bonding
2024-04-24 15:09:51 +00:00
import this c53b46ee1d [DOCs]: HOTFix CI/CD GH runners (#4554)
* build on master first

* rename pages to unique names

* rename pages to unique names

* module updates

* rm old cmrdun and fix modules

* syntax fix

* comment out cmdrun

* comment out cmdrun

* feedback fix

* TEMP hack to build books

* adding sleep to build script

* adding test to mdbook script

* TEMP hack to build books

* add cmdrun back

* add cmdrun back

* final commit - ready to go
2024-04-24 15:00:56 +02:00
benedetta davico 7fc9eca46f Update publish-nym-binaries.yml 2024-04-24 11:28:04 +02:00
Jon Häggblad 4e5c765a0d Quickfix for unused warnings after manually disabling legacy routes for release (#4557) 2024-04-23 16:24:59 +02:00
Jon Häggblad e1abbc0b5b Disable legacy endpoints in mixnode http client (#4556) 2024-04-23 13:46:02 +02:00
import this ce067db401 [DOCs]: Guides for NYM-NODE (#4541)
* initialise nym node pages

* new TOC flow and archive section

* vps setup page draft

* syntax fix

* syntax fix

* syntax fix

* add ulimit setup

* initialise nym-node page

* add ports

* syntax edit

* syntax edit

* add flags

* add setup, run and bonding pages

* add info

* syntax edit

* add configuration page

* syntax fix

* syntax fix

* add vps troubleshooting

* add vps troubleshooting

* create nyx configuration page

* add quickflow

* add quickflow

* add quickflow

* add quickflow

* add quickflow

* add quickflow

* syntax fix

* syntax fix

* move vps setup to configuration

* syntax fix

* syntax fix

* syntax fix

* edit points

* finish nym-node setup.md page

* new connnectivity configuration - page finished

* finish proxy and landing page guide

* finish nymvisor update

* finish performance testing upgdate

* finish faq pages update

* fix troubleshooting

* fix troubleshooting

* fix manual-upgrade

* finish introduction page

* update introduction

* update summary

* correct all links

* update graphs

* update cmdruns

* syntax edit and spellcheck

* updated mdbook plugins

* mdbook update

* update modules

* remove redundant

* fix version cmdrun

* removed smoosh-faw to archive

* syntax fix

* attempt to update mdbook admonish

* try dirty workaround hack

* try dirty workaround hack

* try dirty workaround hack

* try dirty workaround hack

* try dirty workaround hack

* try dirty workaround hack

* try dirty workaround hack

* try dirty workaround hack

* try dirty workaround hack

* address review comments

* address review comments

* address review comments

* PR ready to merge
2024-04-23 09:14:27 +00:00
Jon Häggblad 373cc54f3f cargo update -p rustls@0.21.10 (#4551) 2024-04-22 16:29:19 +02:00
benedettadavico a276608fd0 updating versions and changelog 2024-04-22 15:44:16 +02:00
Jędrzej Stuczyński b332a6b556 attach 'last_polled' metadata to node descriptions (#4550) 2024-04-22 09:54: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
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
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
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
fmtabbara 635ae1118a Add mobile delegations beta banner 2024-03-22 12:19:54 +00: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
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
fmtabbara e699f1ad79 explorer env configuration updates 2024-03-20 13:45:29 +00: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
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
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
Sachin Kamath 1ebb0c7daa don't use bigdecimal 2024-03-18 20:28:55 +05:30
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
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
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
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
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
fmtabbara 28a0c3eb17 Add buffer module to webpack.common.js 2024-02-28 11:50:19 +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
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
690 changed files with 27465 additions and 11941 deletions
+18 -3
View File
@@ -9,7 +9,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git
- name: Install rsync
run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x
@@ -30,9 +30,24 @@ jobs:
- name: Remove existing Nym config directory (`~/.nym/`)
run: cd documentation && ./remove_existing_config.sh
continue-on-error: false
- name: Build all projects in documentation/ & move to ~/dist/docs/
# This is the original flow
# - name: Build all projects in documentation/ & move to ~/dist/docs/
# run: cd documentation && ./build_all_to_dist.sh
# This is a workaround replacement which builds on the last working commit b332a6b55668f60988e36961f3f62a794ba82ddb and then on current branch
- name: Save current branch to ~/current_branch
run: git rev-parse --abbrev-ref HEAD > ~/current_branch
- name: Git pull & switch to b332a6b55668f60988e36961f3f62a794ba82ddb
run: git pull && git checkout b332a6b55668f60988e36961f3f62a794ba82ddb
- name: Build all projects in documentation/ & move to ~/dist/docs/ from b332a6b55668f60988e36961f3f62a794ba82ddb
run: cd documentation && ./build_all_to_dist.sh
continue-on-error: false
- name: Switch to current branch
run: git checkout $echo "$(cat ~/current_branch)"
- name: Build all projects in documentation/ & move to ~/dist/docs/ on current branch
run: cd documentation && ./build_all_to_dist.sh && rm ~/current_branch
# End of replacemet
- name: Post process
run: cd documentation && ./post_process.sh
@@ -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
+19 -3
View File
@@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git
- name: Install rsync
run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x
@@ -34,9 +34,25 @@ jobs:
- name: Remove existing Nym config directory (`~/.nym/`)
run: cd documentation && ./remove_existing_config.sh
continue-on-error: false
- name: Build all projects in documentation/ & move to ~/dist/docs/
# This is the original flow
# - name: Build all projects in documentation/ & move to ~/dist/docs/
# run: cd documentation && ./build_all_to_dist.sh
# This is a workaround replacement which builds on the last working commit b332a6b55668f60988e36961f3f62a794ba82ddb and then on current branch
- name: Save current branch to ~/current_branch
run: git rev-parse --abbrev-ref HEAD > ~/current_branch
- name: Git pull & switch to b332a6b55668f60988e36961f3f62a794ba82ddb
run: git pull && git checkout b332a6b55668f60988e36961f3f62a794ba82ddb
- name: Build all projects in documentation/ & move to ~/dist/docs/ from b332a6b55668f60988e36961f3f62a794ba82ddb
run: cd documentation && ./build_all_to_dist.sh
continue-on-error: false
- name: Switch to current branch
run: git checkout $echo "$(cat ~/current_branch)"
- name: Build all projects in documentation/ & move to ~/dist/docs/ on current branch
run: cd documentation && ./build_all_to_dist.sh && rm ~/current_branch
# End of replacemet
- name: Deploy branch to CI www
continue-on-error: true
@@ -30,6 +30,7 @@ jobs:
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
nymvisor_hash: ${{ steps.binary-hashes.outputs.nymvisor_hash }}
nymnode_hash: ${{ steps.binary-hashes.outputs.nymnode_hash }}
socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }}
netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }}
cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }}
@@ -38,6 +39,7 @@ jobs:
mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }}
gateway_version: ${{ steps.binary-versions.outputs.gateway_version }}
nymvisor_version: ${{ steps.binary-versions.outputs.nymvisor_version }}
nymnode_version: ${{ steps.binary-versions.outputs.nymnode_version }}
socks5_version: ${{ steps.binary-versions.outputs.socks5_version }}
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
@@ -81,6 +83,7 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
retention-days: 30
- id: create-release
@@ -99,6 +102,7 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
+14
View File
@@ -4,6 +4,20 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.3-eclipse] (2024-04-22)
- Initial release of the first iteration of the Nym Node
- Improvements to gateway functionality
- IPR development
- Removal of allow list in favour of implementing an exit policy
- Explorer delegation: enables direct delegation to nodes via the Nym Explorer
## [2024.2-fast-and-furious] (2024-03-25)
- Internal testing pre-release
## [2024.1-marabou] (2024-02-15)
**New Features:**
Generated
+493 -192
View File
File diff suppressed because it is too large Load Diff
+17 -10
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",
@@ -39,6 +42,7 @@ members = [
"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",
@@ -102,6 +107,7 @@ 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",
@@ -116,7 +122,6 @@ members = [
# "wasm/full-nym-wasm",
"wasm/mix-fetch",
"wasm/node-tester",
"common/nym-metrics",
]
default-members = [
@@ -130,6 +135,7 @@ default-members = [
"tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
"nym-node"
]
exclude = [
@@ -165,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"
@@ -202,19 +209,19 @@ 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
+3 -3
View File
@@ -1,10 +1,10 @@
[package]
name = "nym-client"
version = "1.1.33"
version = "1.1.34"
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" }
+291 -91
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",
@@ -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",
@@ -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"
@@ -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",
@@ -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",
@@ -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": {
+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,54 +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 nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportCredentialArgs,
};
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
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)]
pub(crate) version: Option<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())?
}
};
import_credential(credentials_store, raw_credential, args.version).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
}
+3
View File
@@ -23,6 +23,9 @@ pub enum ClientError {
#[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode,
#[error(transparent)]
ConfigUpgradeFailure(#[from] nym_client_core::config::ConfigUpgradeFailure),
#[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 -3
View File
@@ -1,10 +1,10 @@
[package]
name = "nym-socks5-client"
version = "1.1.33"
version = "1.1.34"
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" }
@@ -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,54 +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 nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportCredentialArgs,
};
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
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)]
pub(crate) version: Option<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())?
}
};
import_credential(credentials_store, raw_credential, args.version).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
+3
View File
@@ -23,6 +23,9 @@ pub enum Socks5ClientError {
#[error(transparent)]
ClientCoreError(#[from] ClientCoreError),
#[error(transparent)]
ConfigUpgradeFailure(#[from] nym_client_core::config::ConfigUpgradeFailure),
#[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"
+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))
}
}
+12 -21
View File
@@ -11,29 +11,30 @@ 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"
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" }
@@ -47,7 +48,9 @@ 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]
@@ -74,11 +77,6 @@ features = ["time"]
version = "0.20.1"
features = ["rustls-tls-native-roots"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
workspace = true
@@ -104,17 +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.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(())
}
@@ -3,11 +3,12 @@ use std::{
time::{Duration, Instant},
};
use log::{info, warn};
use nym_metrics::{inc, inc_by, metrics};
use nym_metrics::{inc, inc_by};
use si_scale::helpers::bibytes2;
// Metrics server
use futures::future::{FusedFuture, OptionFuture};
use futures::FutureExt;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use http_body_util::Full;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
@@ -23,6 +24,7 @@ use hyper_util::rt::TokioIo;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use std::convert::Infallible;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[cfg(feature = "metrics-server")]
use std::net::SocketAddr;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use tokio::net::TcpListener;
@@ -508,17 +510,17 @@ impl PacketStatisticsControl {
cfg_if::cfg_if! {
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
log::warn!("Metrics server is not supported on wasm32-unknown-unknown");
let listener = None;
} else {
let listener: Option<WasmEmpty> = None;
} else if #[cfg(feature = "metrics-server")] {
let mut metrics_port = 18000;
let listener: Option<TcpListener>;
loop {
let addr = SocketAddr::from(([0, 0, 0, 0], metrics_port));
match TcpListener::bind(addr).await {
Ok(l) => {
info!("###############################");
info!("Metrics endpoint is at: {:?}", l.local_addr());
info!("###############################");
log::info!("###############################");
log::info!("Metrics endpoint is at: {:?}", l.local_addr());
log::info!("###############################");
listener = Some(l);
break;
},
@@ -528,11 +530,21 @@ impl PacketStatisticsControl {
}
};
}
} else {
log::info!("Metrics server is disabled!");
let listener: Option<TcpListener> = None;
}
}
loop {
// it seems at some point tokio changed its select precondition evaluation,
// and it's no longer checked before the future is evaluated.
let accept_future: OptionFuture<_> = listener
.as_ref()
.map(|l| l.accept())
.map(FutureExt::fuse)
.into();
tokio::select! {
stats_event = self.stats_rx.recv() => match stats_event {
Some(stats_event) => {
@@ -545,10 +557,11 @@ impl PacketStatisticsControl {
}
},
// conditional will disable the branch if we're in wasm32-unknown-unknown
result = listener.as_ref().unwrap().accept(), if listener.is_some() => {
// use `_` to calm down clippy when running for wasm
_result = accept_future, if !accept_future.is_terminated() => {
cfg_if::cfg_if! {
if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
if let Ok((stream, _)) = result {
if let Some(Ok((stream, _))) = _result {
let io = TokioIo::new(stream);
tokio::task::spawn(async move {
@@ -556,11 +569,11 @@ impl PacketStatisticsControl {
.serve_connection(io, service_fn(serve_metrics))
.await
{
warn!("Error serving connection: {:?}", err);
log::warn!("Error serving connection: {:?}", err);
}
});
} else {
warn!("Error accepting connection");
log::warn!("Error accepting connection");
}
}
}
@@ -590,8 +603,19 @@ impl PacketStatisticsControl {
}
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
async fn serve_metrics(
_: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
use nym_metrics::metrics;
Ok(Response::new(Full::new(Bytes::from(metrics!()))))
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
struct WasmEmpty;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
impl WasmEmpty {
async fn accept(&self) {}
}
+3 -1
View File
@@ -2,4 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
pub mod reply_controller;
pub mod reply_storage;
// re-export it under the old name to preserve import paths
pub use nym_client_core_surb_storage as reply_storage;
@@ -1,6 +1,5 @@
use std::{collections::HashMap, fmt};
use log::{debug, error, info};
use crate::config::GroupBy;
use log::{debug, error};
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
use nym_network_defaults::var_names::EXPLORER_API;
use nym_topology::{
@@ -10,11 +9,11 @@ use nym_topology::{
};
use nym_validator_client::client::MixId;
use rand::{prelude::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tap::TapOptional;
use url::Url;
use crate::config::GroupBy;
pub use nym_country_group::CountryGroup;
const MIN_NODES_PER_LAYER: usize = 1;
@@ -38,158 +37,6 @@ fn create_explorer_client() -> Option<ExplorerClient> {
Some(client)
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum CountryGroup {
Europe,
NorthAmerica,
SouthAmerica,
Oceania,
Asia,
Africa,
Unknown,
}
impl CountryGroup {
// We map contry codes into group, which initially are continent codes to a first approximation,
// but we do it manually to reserve the right to tweak this distribution for our purposes.
// NOTE: I did this quickly and it's not a complete list of all countries, but only those that
// were present in the network at the time. Please add more as needed.
fn new(country_code: &str) -> Self {
let country_code = country_code.to_uppercase();
use CountryGroup::*;
match country_code.as_ref() {
// Europe
"AT" => Europe,
"BG" => Europe,
"CH" => Europe,
"CY" => Europe,
"CZ" => Europe,
"DE" => Europe,
"DK" => Europe,
"ES" => Europe,
"FI" => Europe,
"FR" => Europe,
"GB" => Europe,
"GR" => Europe,
"IE" => Europe,
"IT" => Europe,
"LT" => Europe,
"LU" => Europe,
"LV" => Europe,
"MD" => Europe,
"MT" => Europe,
"NL" => Europe,
"NO" => Europe,
"PL" => Europe,
"RO" => Europe,
"SE" => Europe,
"SK" => Europe,
"TR" => Europe,
"UA" => Europe,
// North America
"CA" => NorthAmerica,
"MX" => NorthAmerica,
"US" => NorthAmerica,
// South America
"AR" => SouthAmerica,
"BR" => SouthAmerica,
"CL" => SouthAmerica,
"CO" => SouthAmerica,
"CR" => SouthAmerica,
"GT" => SouthAmerica,
// Oceania
"AU" => Oceania,
// Asia
"AM" => Asia,
"BH" => Asia,
"CN" => Asia,
"GE" => Asia,
"HK" => Asia,
"ID" => Asia,
"IL" => Asia,
"IN" => Asia,
"JP" => Asia,
"KH" => Asia,
"KR" => Asia,
"KZ" => Asia,
"MY" => Asia,
"RU" => Asia,
"SG" => Asia,
"TH" => Asia,
"VN" => Asia,
// Africa
"SC" => Africa,
"UG" => Africa,
"ZA" => Africa,
// And group level codes work too
"EU" => Europe,
"NA" => NorthAmerica,
"SA" => SouthAmerica,
"OC" => Oceania,
"AS" => Asia,
"AF" => Africa,
// And some aliases
"EUROPE" => Europe,
"NORTHAMERICA" => NorthAmerica,
"SOUTHAMERICA" => SouthAmerica,
"OCEANIA" => Oceania,
"ASIA" => Asia,
"AFRICA" => Africa,
_ => {
info!("Unknown country code: {}", country_code);
Unknown
}
}
}
}
impl fmt::Display for CountryGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use CountryGroup::*;
match self {
Europe => write!(f, "EU"),
NorthAmerica => write!(f, "NA"),
SouthAmerica => write!(f, "SA"),
Oceania => write!(f, "OC"),
Asia => write!(f, "AS"),
Africa => write!(f, "AF"),
Unknown => write!(f, "Unknown"),
}
}
}
impl std::str::FromStr for CountryGroup {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let group = CountryGroup::new(s);
if group == CountryGroup::Unknown {
Err(())
} else {
Ok(group)
}
}
}
impl CountryGroup {
#[allow(unused)]
fn known(self) -> Option<CountryGroup> {
use CountryGroup::*;
match self {
Europe | NorthAmerica | SouthAmerica | Oceania | Asia | Africa => Some(self),
Unknown => None,
}
}
}
fn group_mixnodes_by_country_code(
mixnodes: Vec<PrettyDetailedMixNodeBond>,
) -> HashMap<CountryGroup, Vec<MixId>> {
@@ -16,6 +16,8 @@ const OLDEST_LANE_SET_SIZE: usize = 4;
// As a way of prune connections we also check for timeouts.
const MSG_CONSIDERED_STALE_AFTER_SECS: u64 = 10 * 60;
// this trait is apparently not used in wasm
#[allow(dead_code)]
pub(crate) trait SizedData {
fn data_size(&self) -> usize;
}
@@ -1,42 +0,0 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
pub mod keys_paths;
pub mod old_v1_1_20_2;
pub const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPaths {
pub keys: ClientKeysPaths,
/// 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 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_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
keys: ClientKeysPaths::new_base(base_data_directory),
}
}
}
@@ -1,32 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
use crate::config::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAY_DETAILS_FILENAME};
use crate::error::ClientCoreError;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPathsV1_1_20_2 {
pub keys: ClientKeysPaths,
pub credentials_database: PathBuf,
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV1_1_20_2 {
pub fn upgrade_default(self) -> Result<CommonClientPaths, ClientCoreError> {
let data_dir = self.reply_surb_database.parent().ok_or_else(|| {
ClientCoreError::UnableToUpgradeConfigFile {
new_version: "1.1.20-2".to_string(),
}
})?;
Ok(CommonClientPaths {
keys: self.keys,
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
credentials_database: self.credentials_database,
reply_surb_database: self.reply_surb_database,
})
}
}
+6 -674
View File
@@ -1,677 +1,9 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError};
use nym_config::defaults::NymNetworkDetails;
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::GatewayConfig;
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
pub use nym_client_core_config_types::disk_persistence;
pub use nym_client_core_config_types::old::{
old_config_v1_1_13, old_config_v1_1_20, old_config_v1_1_20_2, old_config_v1_1_30,
old_config_v1_1_33,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
pub mod disk_persistence;
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;
// '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);
#[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(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(getter_with_clone))]
pub struct GatewayEndpointConfig {
/// 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,
}
impl TryFrom<GatewayEndpointConfig> for GatewayConfig {
type Error = ClientCoreError;
fn try_from(value: GatewayEndpointConfig) -> Result<Self, Self::Error> {
Ok(GatewayConfig {
gateway_identity: identity::PublicKey::from_base58_string(value.gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?,
gateway_owner: Some(value.gateway_owner),
gateway_listener: value.gateway_listener,
})
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
impl GatewayEndpointConfig {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
pub fn new(
gateway_id: String,
gateway_owner: String,
gateway_listener: String,
) -> GatewayEndpointConfig {
GatewayEndpointConfig {
gateway_id,
gateway_owner,
gateway_listener,
}
}
}
// separate block so it wouldn't be exported via wasm bindgen
impl GatewayEndpointConfig {
pub fn try_get_gateway_identity_key(&self) -> Result<identity::PublicKey, ClientCoreError> {
identity::PublicKey::from_base58_string(&self.gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
}
pub fn from_node(
node: nym_topology::gateway::Node,
must_use_tls: bool,
) -> Result<Self, ClientCoreError> {
let gateway_listener = if must_use_tls {
node.clients_address_tls()
.ok_or(ClientCoreError::UnsupportedWssProtocol {
gateway: node.identity_key.to_base58_string(),
})?
} else {
node.clients_address()
};
Ok(GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener,
gateway_owner: node.owner,
})
}
}
#[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,
disabled_credentials_mode: bool,
nyxd_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
) -> Self {
Client {
version: env!("CARGO_PKG_VERSION").to_string(),
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(),
}
}
}
pub use nym_client_core_config_types::*;
+56 -3
View File
@@ -39,6 +39,9 @@ pub enum ClientCoreError {
#[error("no gateways on network")]
NoGatewaysOnNetwork,
#[error("there are no more new gateways on the network - it seems this client has already registered with all nodes it could have")]
NoNewGatewaysAvailable,
#[error("list of nym apis is empty")]
ListOfNymApisIsEmpty,
@@ -55,8 +58,8 @@ pub enum ClientCoreError {
source: Box<dyn Error + Send + Sync>,
},
#[error("experienced a failure with our gateway details storage: {source}")]
GatewayDetailsStoreError {
#[error("experienced a failure with our gateways details storage: {source}")]
GatewaysDetailsStoreError {
source: Box<dyn Error + Send + Sync>,
},
@@ -94,13 +97,21 @@ pub enum ClientCoreError {
#[error("unexpected exit")]
UnexpectedExit,
#[error("this operation would have resulted in the gateway {gateway_id:?} key being overwritten without permission")]
ForbiddenGatewayKeyOverwrite { gateway_id: String },
#[error(
"this operation would have resulted in clients keys being overwritten without permission"
)]
ForbiddenKeyOverwrite,
#[error("gateway details are unavailable")]
#[error("the client doesn't have any gateway set as active")]
NoActiveGatewaySet,
#[error("gateway details for gateway {gateway_id:?} are unavailable")]
UnavailableGatewayDetails {
gateway_id: String,
#[source]
source: Box<dyn Error + Send + Sync>,
},
@@ -113,9 +124,15 @@ pub enum ClientCoreError {
#[error("the provided gateway details (for gateway {gateway_id}) do not correspond to the shared keys")]
MismatchedGatewayDetails { gateway_id: String },
#[error("unable to upgrade config file from `{current_version}`")]
ConfigFileUpgradeFailure { current_version: String },
#[error("unable to upgrade config file to `{new_version}`")]
UnableToUpgradeConfigFile { new_version: String },
#[error("failed to upgrade config file: {message}")]
UpgradeFailure { message: String },
#[error("the provided gateway details don't much the stored data")]
MismatchedStoredGatewayDetails,
@@ -153,6 +170,42 @@ pub enum ClientCoreError {
#[source]
source: std::io::Error,
},
#[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: {err}")]
MalformedGatewayOwnerAccountAddress {
gateway_id: String,
raw_owner: String,
// just use the string formatting as opposed to underlying type to avoid having to import cosmrs
err: String,
},
#[error(
"the listening address of gateway {gateway_id} ({raw_listener}) is malformed: {source}"
)]
MalformedListener {
gateway_id: String,
raw_listener: String,
#[source]
source: url::ParseError,
},
#[error("this client (id: '{client_id}') has already been initialised before. If you want to add additional gateway, use `add-gateway` command")]
AlreadyInitialised { client_id: String },
#[error("this client has already registered with gateway {gateway_id}")]
AlreadyRegistered { gateway_id: String },
}
/// Set of messages that the client can send to listeners via the task manager
+12 -14
View File
@@ -1,7 +1,6 @@
// Copyright 2022-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::RegistrationResult;
use futures::{SinkExt, StreamExt};
@@ -9,6 +8,7 @@ use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient;
use nym_topology::{filter::VersionFilterable, gateway, mix};
use nym_validator_client::client::IdentityKeyRef;
use rand::{seq::SliceRandom, Rng};
use std::{sync::Arc, time::Duration};
use tungstenite::Message;
@@ -17,16 +17,13 @@ use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::Instant;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
use nym_validator_client::client::IdentityKeyRef;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
#[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket;
@@ -35,6 +32,9 @@ use wasmtimer::std::Instant;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep;
#[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
#[cfg(target_arch = "wasm32")]
type WsConn = JSWebsocket;
@@ -278,16 +278,17 @@ pub(super) fn get_specified_gateway(
}
pub(super) async fn register_with_gateway(
gateway: &GatewayEndpointConfig,
gateway_id: identity::PublicKey,
gateway_listener: Url,
our_identity: Arc<identity::KeyPair>,
) -> Result<RegistrationResult, ClientCoreError> {
let mut gateway_client =
GatewayClient::new_init(gateway.to_owned().try_into()?, our_identity.clone());
GatewayClient::new_init(gateway_listener, gateway_id, our_identity.clone());
gateway_client.establish_connection().await.map_err(|err| {
log::warn!("Failed to establish connection with gateway!");
ClientCoreError::GatewayClientError {
gateway_id: gateway.gateway_id.clone(),
gateway_id: gateway_id.to_base58_string(),
source: err,
}
})?;
@@ -295,12 +296,9 @@ pub(super) async fn register_with_gateway(
.perform_initial_authentication()
.await
.map_err(|err| {
log::warn!(
"Failed to register with the gateway {}!",
gateway.gateway_id
);
log::warn!("Failed to register with the gateway {gateway_id}: {err}");
ClientCoreError::GatewayClientError {
gateway_id: gateway.gateway_id.clone(),
gateway_id: gateway_id.to_base58_string(),
source: err,
}
})?;
+117 -124
View File
@@ -1,243 +1,236 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Collection of initialization steps used by client implementations
use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
use crate::client::base_client::storage::helpers::{
has_gateway_details, load_active_gateway_details, load_client_keys, load_gateway_details,
store_gateway_details,
};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys;
use crate::config::GatewayEndpointConfig;
use crate::client::key_manager::ClientKeys;
use crate::error::ClientCoreError;
use crate::init::helpers::{
choose_gateway_by_latency, get_specified_gateway, uniformly_random_gateway,
};
use crate::init::types::{
CustomGatewayDetails, GatewayDetails, GatewaySelectionSpecification, GatewaySetup,
InitialisationResult,
GatewaySelectionSpecification, GatewaySetup, InitialisationResult, SelectedGateway,
};
use nym_client_core_gateways_storage::GatewaysDetailsStore;
use nym_client_core_gateways_storage::{GatewayDetails, GatewayRegistration};
use nym_gateway_client::client::InitGatewayClient;
use nym_topology::gateway;
use rand::rngs::OsRng;
use serde::de::DeserializeOwned;
use rand::{CryptoRng, RngCore};
use serde::Serialize;
use std::sync::Arc;
use std::net::IpAddr;
pub mod helpers;
pub mod types;
// helpers for error wrapping
async fn _store_gateway_details<T, D>(
details_store: &D,
details: &PersistedGatewayDetails<T>,
pub async fn generate_new_client_keys<K, R>(
rng: &mut R,
key_store: &K,
) -> Result<(), ClientCoreError>
where
D: GatewayDetailsStore<T>,
D::StorageError: Send + Sync + 'static,
T: Serialize + Send + Sync,
{
details_store
.store_gateway_details(details)
.await
.map_err(|source| ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
}
async fn _load_gateway_details<T, D>(
details_store: &D,
) -> Result<PersistedGatewayDetails<T>, ClientCoreError>
where
D: GatewayDetailsStore<T>,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
details_store
.load_gateway_details()
.await
.map_err(|source| ClientCoreError::UnavailableGatewayDetails {
source: Box::new(source),
})
}
async fn _load_managed_keys<K>(key_store: &K) -> Result<ManagedKeys, ClientCoreError>
where
R: RngCore + CryptoRng,
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
ManagedKeys::try_load(key_store)
ClientKeys::generate_new(rng)
.persist_keys(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})
}
fn ensure_valid_details<T>(
details: &PersistedGatewayDetails<T>,
loaded_keys: &ManagedKeys,
) -> Result<(), ClientCoreError> {
details.validate(loaded_keys.gateway_shared_key().as_deref())
}
async fn setup_new_gateway<T, K, D>(
async fn setup_new_gateway<K, D>(
key_store: &K,
details_store: &D,
overwrite_data: bool,
selection_specification: GatewaySelectionSpecification<T>,
selection_specification: GatewaySelectionSpecification,
available_gateways: Vec<gateway::Node>,
) -> Result<InitialisationResult<T>, ClientCoreError>
wg_tun_ip_address: Option<IpAddr>,
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewaysDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
log::trace!("Setting up new gateway");
// if we're setting up new gateway, failing to load existing information is fine.
// as a matter of fact, it's only potentially a problem if we DO succeed
if _load_gateway_details(details_store).await.is_ok() && !overwrite_data {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
if _load_managed_keys(key_store).await.is_ok() && !overwrite_data {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
// if we're setting up new gateway, we must have had generated long-term client keys before
let client_keys = load_client_keys(key_store).await?;
let mut rng = OsRng;
let mut new_keys = ManagedKeys::generate_new(&mut rng);
let gateway_details = match selection_specification {
let selected_gateway = match selection_specification {
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)?
}
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
let gateway =
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)?
}
GatewaySelectionSpecification::Specified {
must_use_tls,
identity,
} => {
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)?
}
GatewaySelectionSpecification::Custom {
gateway_identity,
additional_data,
} => GatewayDetails::Custom(CustomGatewayDetails::new(gateway_identity, additional_data)),
} => SelectedGateway::custom(gateway_identity, additional_data)?,
};
let registration_result = if let GatewayDetails::Configured(gateway_cfg) = &gateway_details {
// if we're using a 'normal' gateway setup, do register
let our_identity = new_keys.identity_keypair();
Some(helpers::register_with_gateway(gateway_cfg, our_identity).await?)
} else {
None
// check if we already have details associated with this particular gateway
// and if so, see if we can overwrite it
let selected_id = selected_gateway.gateway_id().to_base58_string();
if has_gateway_details(details_store, &selected_id).await? {
return Err(ClientCoreError::AlreadyRegistered {
gateway_id: selected_id,
});
}
let (gateway_details, authenticated_ephemeral_client) = match selected_gateway {
SelectedGateway::Remote {
gateway_id,
gateway_owner_address,
gateway_listener,
wg_tun_address,
} => {
// if we're using a 'normal' gateway setup, do register
let our_identity = client_keys.identity_keypair();
// if wg address is set, use that one
let url = wg_tun_address.clone().unwrap_or(gateway_listener.clone());
let registration =
helpers::register_with_gateway(gateway_id, url, our_identity).await?;
(
GatewayDetails::new_remote(
gateway_id,
registration.shared_keys,
gateway_owner_address,
gateway_listener,
wg_tun_address,
),
Some(registration.authenticated_ephemeral_client),
)
}
SelectedGateway::Custom {
gateway_id,
additional_data,
} => (
GatewayDetails::new_custom(gateway_id, additional_data),
None,
),
};
let maybe_shared_keys = registration_result
.as_ref()
.map(|r| Arc::clone(&r.shared_keys));
let gateway_registration = gateway_details.into();
let persisted_details =
PersistedGatewayDetails::new(gateway_details, maybe_shared_keys.as_deref())?;
// persist the keys
new_keys
.deal_with_gateway_key(maybe_shared_keys, key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
// persist gateway configs
_store_gateway_details(details_store, &persisted_details).await?;
// persist gateway details
store_gateway_details(details_store, &gateway_registration).await?;
Ok(InitialisationResult {
gateway_details: persisted_details.into(),
managed_keys: new_keys,
authenticated_ephemeral_client: registration_result
.map(|r| r.authenticated_ephemeral_client),
gateway_registration,
client_keys,
authenticated_ephemeral_client,
})
}
async fn use_loaded_gateway_details<T, K, D>(
async fn use_loaded_gateway_details<K, D>(
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
gateway_id: Option<String>,
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewaysDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
let loaded_details = if let Some(gateway_id) = gateway_id {
load_gateway_details(details_store, &gateway_id).await?
} else {
load_active_gateway_details(details_store)
.await?
.registration
.ok_or(ClientCoreError::NoActiveGatewaySet)?
};
ensure_valid_details(&loaded_details, &loaded_keys)?;
let loaded_keys = load_client_keys(key_store).await?;
// no need to persist anything as we got everything from the storage
Ok(InitialisationResult::new_loaded(
loaded_details.into(),
loaded_details,
loaded_keys,
))
}
fn reuse_gateway_connection<T>(
fn reuse_gateway_connection(
authenticated_ephemeral_client: InitGatewayClient,
gateway_details: GatewayDetails<T>,
managed_keys: ManagedKeys,
) -> InitialisationResult<T> {
gateway_registration: GatewayRegistration,
client_keys: ClientKeys,
) -> InitialisationResult {
InitialisationResult {
gateway_details,
managed_keys,
gateway_registration,
client_keys,
authenticated_ephemeral_client: Some(authenticated_ephemeral_client),
}
}
pub async fn setup_gateway<T, K, D>(
setup: GatewaySetup<T>,
pub async fn setup_gateway<K, D>(
setup: GatewaySetup,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewaysDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
log::debug!("Setting up gateway");
match setup {
GatewaySetup::MustLoad => use_loaded_gateway_details(key_store, details_store).await,
GatewaySetup::MustLoad { gateway_id } => {
log::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}");
use_loaded_gateway_details(key_store, details_store, gateway_id).await
}
GatewaySetup::New {
specification,
available_gateways,
overwrite_data,
wg_tun_address,
} => {
log::debug!("GatewaySetup::New with spec: {specification:?}");
setup_new_gateway(
key_store,
details_store,
overwrite_data,
specification,
available_gateways,
wg_tun_address,
)
.await
}
GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details,
managed_keys,
} => Ok(reuse_gateway_connection(
authenticated_ephemeral_client,
gateway_details,
managed_keys,
)),
client_keys: managed_keys,
} => {
log::debug!("GatewaySetup::ReuseConnection");
Ok(reuse_gateway_connection(
authenticated_ephemeral_client,
*gateway_details,
managed_keys,
))
}
}
}
+183 -157
View File
@@ -1,24 +1,132 @@
// 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::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedCustomGatewayDetails, PersistedGatewayDetails,
};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys;
use crate::config::{Config, GatewayEndpointConfig};
use crate::client::key_manager::ClientKeys;
use crate::config::Config;
use crate::error::ClientCoreError;
use crate::init::{_load_gateway_details, _load_managed_keys, setup_gateway};
use crate::init::{setup_gateway, use_loaded_gateway_details};
use log::info;
use nym_client_core_gateways_storage::{
GatewayRegistration, GatewaysDetailsStore, RemoteGatewayDetails,
};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::InitGatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use nym_validator_client::nyxd::AccountId;
use serde::Serialize;
use std::fmt::Display;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc;
use time::OffsetDateTime;
use url::Url;
pub enum SelectedGateway {
Remote {
gateway_id: identity::PublicKey,
gateway_owner_address: AccountId,
gateway_listener: Url,
wg_tun_address: Option<Url>,
},
Custom {
gateway_id: identity::PublicKey,
additional_data: Option<Vec<u8>>,
},
}
fn wg_tun_address(
tun_ip: Option<IpAddr>,
gateway: &gateway::Node,
) -> Result<Option<Url>, ClientCoreError> {
let Some(tun_ip) = tun_ip else {
return Ok(None);
};
// log this so we'd remember about it if we ever decided to actually use that port
if gateway.clients_wss_port.is_some() {
info!(
"gateway {} exposes wss but for wireguard we're going to use ws",
gateway.identity_key
);
}
let raw_url = format!("ws://{tun_ip}:{}", gateway.clients_ws_port);
Ok(Some(raw_url.as_str().parse().map_err(|source| {
ClientCoreError::MalformedListener {
gateway_id: gateway.identity_key.to_base58_string(),
raw_listener: raw_url,
source,
}
})?))
}
impl SelectedGateway {
pub fn from_topology_node(
node: gateway::Node,
wg_tun_ip_address: Option<IpAddr>,
must_use_tls: bool,
) -> Result<Self, ClientCoreError> {
let gateway_listener = if must_use_tls {
node.clients_address_tls()
.ok_or(ClientCoreError::UnsupportedWssProtocol {
gateway: node.identity_key.to_base58_string(),
})?
} else {
node.clients_address()
};
let wg_tun_address = wg_tun_address(wg_tun_ip_address, &node)?;
let gateway_owner_address = AccountId::from_str(&node.owner).map_err(|source| {
ClientCoreError::MalformedGatewayOwnerAccountAddress {
gateway_id: node.identity_key.to_base58_string(),
raw_owner: node.owner,
err: source.to_string(),
}
})?;
let gateway_listener =
Url::parse(&gateway_listener).map_err(|source| ClientCoreError::MalformedListener {
gateway_id: node.identity_key.to_base58_string(),
raw_listener: gateway_listener,
source,
})?;
Ok(SelectedGateway::Remote {
gateway_id: node.identity_key,
gateway_owner_address,
gateway_listener,
wg_tun_address,
})
}
pub fn custom(
gateway_id: String,
additional_data: Option<Vec<u8>>,
) -> Result<Self, ClientCoreError> {
let gateway_id = identity::PublicKey::from_base58_string(&gateway_id)
.map_err(|source| ClientCoreError::MalformedGatewayIdentity { gateway_id, source })?;
Ok(SelectedGateway::Custom {
gateway_id,
additional_data,
})
}
pub fn gateway_id(&self) -> &identity::PublicKey {
match self {
SelectedGateway::Remote { gateway_id, .. } => gateway_id,
SelectedGateway::Custom { gateway_id, .. } => gateway_id,
}
}
}
/// Result of registering with a gateway:
/// - shared keys derived between ourselves and the node
@@ -33,17 +141,17 @@ pub struct RegistrationResult {
/// - all loaded (or derived) keys
/// - an optional authenticated handle of an ephemeral gateway handle created for the purposes of registration,
/// if this was the first time this client registered
pub struct InitialisationResult<T = EmptyCustomDetails> {
pub gateway_details: GatewayDetails<T>,
pub managed_keys: ManagedKeys,
pub struct InitialisationResult {
pub gateway_registration: GatewayRegistration,
pub client_keys: ClientKeys,
pub authenticated_ephemeral_client: Option<InitGatewayClient>,
}
impl<T> InitialisationResult<T> {
pub fn new_loaded(gateway_details: GatewayDetails<T>, managed_keys: ManagedKeys) -> Self {
impl InitialisationResult {
pub fn new_loaded(gateway_registration: GatewayRegistration, client_keys: ClientKeys) -> Self {
InitialisationResult {
gateway_details,
managed_keys,
gateway_registration,
client_keys,
authenticated_ephemeral_client: None,
}
}
@@ -51,135 +159,30 @@ impl<T> InitialisationResult<T> {
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewaysDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Send + Sync,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
match &loaded_details {
PersistedGatewayDetails::Default(loaded_default) => {
if !loaded_default.verify(&loaded_keys.must_get_gateway_shared_key()) {
return Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: loaded_default.details.gateway_id.clone(),
});
}
}
PersistedGatewayDetails::Custom(_) => {}
}
Ok(InitialisationResult {
gateway_details: loaded_details.into(),
managed_keys: loaded_keys,
authenticated_ephemeral_client: None,
})
use_loaded_gateway_details(key_store, details_store, None).await
}
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
let client_recipient = Recipient::new(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
pub fn client_address(&self) -> Recipient {
Recipient::new(
*self.client_keys.identity_keypair().public_key(),
*self.client_keys.encryption_keypair().public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(self.gateway_details.gateway_id())?,
);
Ok(client_recipient)
}
}
/// Details of particular gateway client got registered with
#[derive(Debug, Clone)]
pub enum GatewayDetails<T = EmptyCustomDetails> {
/// Standard details of a remote gateway
Configured(GatewayEndpointConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(CustomGatewayDetails<T>),
}
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct EmptyCustomDetails {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomGatewayDetails<T = EmptyCustomDetails> {
// whatever custom method is used, gateway's identity must be known
pub gateway_id: String,
#[serde(flatten)]
pub additional_data: T,
}
impl<T> CustomGatewayDetails<T> {
pub fn new(gateway_id: String, additional_data: T) -> Self {
Self {
gateway_id,
additional_data,
}
}
}
impl<T> GatewayDetails<T> {
pub fn try_get_configured_endpoint(&self) -> Option<&GatewayEndpointConfig> {
if let GatewayDetails::Configured(endpoint) = &self {
Some(endpoint)
} else {
None
}
self.gateway_id(),
)
}
pub fn is_custom(&self) -> bool {
matches!(self, GatewayDetails::Custom(_))
}
pub fn gateway_id(&self) -> &str {
match self {
GatewayDetails::Configured(cfg) => &cfg.gateway_id,
GatewayDetails::Custom(custom) => &custom.gateway_id,
}
}
}
impl From<GatewayEndpointConfig> for GatewayDetails {
fn from(value: GatewayEndpointConfig) -> Self {
GatewayDetails::Configured(value)
}
}
impl<T> From<PersistedCustomGatewayDetails<T>> for CustomGatewayDetails<T> {
fn from(value: PersistedCustomGatewayDetails<T>) -> Self {
CustomGatewayDetails {
gateway_id: value.gateway_id,
additional_data: value.additional_data,
}
}
}
impl<T> From<CustomGatewayDetails<T>> for PersistedCustomGatewayDetails<T> {
fn from(value: CustomGatewayDetails<T>) -> Self {
PersistedCustomGatewayDetails {
gateway_id: value.gateway_id,
additional_data: value.additional_data,
}
}
}
impl<T> From<PersistedGatewayDetails<T>> for GatewayDetails<T> {
fn from(value: PersistedGatewayDetails<T>) -> Self {
match value {
PersistedGatewayDetails::Default(default) => {
GatewayDetails::Configured(default.details)
}
PersistedGatewayDetails::Custom(custom) => GatewayDetails::Custom(custom.into()),
}
pub fn gateway_id(&self) -> identity::PublicKey {
self.gateway_registration.details.gateway_id()
}
}
#[derive(Clone, Debug)]
pub enum GatewaySelectionSpecification<T = EmptyCustomDetails> {
pub enum GatewaySelectionSpecification {
/// Uniformly choose a random remote gateway.
UniformRemote { must_use_tls: bool },
@@ -197,11 +200,11 @@ pub enum GatewaySelectionSpecification<T = EmptyCustomDetails> {
/// This client has handled the selection by itself
Custom {
gateway_identity: String,
additional_data: T,
additional_data: Option<Vec<u8>>,
},
}
impl<T> Default for GatewaySelectionSpecification<T> {
impl Default for GatewaySelectionSpecification {
fn default() -> Self {
GatewaySelectionSpecification::UniformRemote {
must_use_tls: false,
@@ -209,7 +212,7 @@ impl<T> Default for GatewaySelectionSpecification<T> {
}
}
impl<T> GatewaySelectionSpecification<T> {
impl GatewaySelectionSpecification {
pub fn new(
gateway_identity: Option<String>,
latency_based_selection: Option<bool>,
@@ -228,19 +231,25 @@ impl<T> GatewaySelectionSpecification<T> {
}
}
pub enum GatewaySetup<T = EmptyCustomDetails> {
pub enum GatewaySetup {
/// The gateway specification (details + keys) MUST BE loaded from the underlying storage.
MustLoad,
MustLoad {
/// Optionally specify concrete gateway id. If none is selected, the current active gateway will be used.
gateway_id: Option<String>,
},
/// Specifies usage of a new gateway
New {
specification: GatewaySelectionSpecification<T>,
specification: GatewaySelectionSpecification,
// TODO: seems to be a bit inefficient to pass them by value
available_gateways: Vec<gateway::Node>,
/// Specifies whether old data should be overwritten whilst setting up new gateway client.
overwrite_data: bool,
/// Implicitly specify whether the chosen gateway must use wireguard mode by setting the tun address.
///
/// Currently this is imperfect solution as I'd imagine this address could vary from gateway to gateway
/// so perhaps it should be part of gateway::Node struct
wg_tun_address: Option<IpAddr>,
},
ReuseConnection {
@@ -248,44 +257,53 @@ pub enum GatewaySetup<T = EmptyCustomDetails> {
authenticated_ephemeral_client: InitGatewayClient,
// Details of this pre-initialised client (i.e. gateway and keys)
gateway_details: GatewayDetails<T>,
gateway_details: Box<GatewayRegistration>,
managed_keys: ManagedKeys,
client_keys: ClientKeys,
},
}
impl<T> GatewaySetup<T> {
pub fn try_reuse_connection(
init_res: InitialisationResult<T>,
) -> Result<Self, ClientCoreError> {
impl GatewaySetup {
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
Ok(GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details: init_res.gateway_details,
managed_keys: init_res.managed_keys,
gateway_details: Box::new(init_res.gateway_registration),
client_keys: init_res.client_keys,
})
} else {
Err(ClientCoreError::NoInitClientPresent)
}
}
/// new gateway setup performed by each client that's inbuilt in a gateway (like NR or IPR)
pub fn new_inbuilt(identity: identity::PublicKey) -> Self {
GatewaySetup::New {
specification: GatewaySelectionSpecification::Custom {
gateway_identity: identity.to_base58_string(),
additional_data: None,
},
available_gateways: vec![],
wg_tun_address: None,
}
}
pub async fn try_setup<K, D>(
self,
key_store: &K,
details_store: &D,
) -> Result<InitialisationResult<T>, ClientCoreError>
) -> Result<InitialisationResult, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore<T>,
D: GatewaysDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
setup_gateway(self, key_store, details_store).await
}
pub fn is_must_load(&self) -> bool {
matches!(self, GatewaySetup::MustLoad)
matches!(self, GatewaySetup::MustLoad { .. })
}
pub fn has_full_details(&self) -> bool {
@@ -302,18 +320,25 @@ pub struct InitResults {
pub encryption_key: String,
pub gateway_id: String,
pub gateway_listener: String,
pub gateway_registration: OffsetDateTime,
pub address: Recipient,
}
impl InitResults {
pub fn new(config: &Config, address: Recipient, gateway: &GatewayEndpointConfig) -> Self {
pub fn new(
config: &Config,
address: Recipient,
gateway: &RemoteGatewayDetails,
registration: OffsetDateTime,
) -> Self {
Self {
version: config.client.version.clone(),
id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: gateway.gateway_id.clone(),
gateway_listener: gateway.gateway_listener.clone(),
gateway_id: gateway.gateway_id.to_base58_string(),
gateway_listener: gateway.gateway_listener.to_string(),
gateway_registration: registration,
address,
}
}
@@ -326,6 +351,7 @@ impl Display for InitResults {
writeln!(f, "Identity key: {}", self.identity_key)?;
writeln!(f, "Encryption: {}", self.encryption_key)?;
writeln!(f, "Gateway ID: {}", self.gateway_id)?;
write!(f, "Gateway: {}", self.gateway_listener)
writeln!(f, "Gateway: {}", self.gateway_listener)?;
write!(f, "Registered at: {}", self.gateway_registration)
}
}
+5 -1
View File
@@ -1,6 +1,10 @@
use std::future::Future;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
pub mod cli_helpers;
pub mod client;
pub mod config;
@@ -0,0 +1,31 @@
[package]
name = "nym-client-core-surb-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
dashmap.workspace = true
log.workspace = true
thiserror.workspace = true
time.workspace = true
nym-crypto = { path = "../../crypto", optional = true, default-features = false }
nym-sphinx = { path = "../../nymsphinx" }
nym-task = { path = "../../task" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
fs-surb-storage = ["sqlx", "nym-crypto", "nym-crypto/hashing"]

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