Compare commits

...

68 Commits

Author SHA1 Message Date
mfahampshire 37c1ca0bd8 lock 2024-05-14 23:23:41 +02:00
mfahampshire 677c3f24a6 got other array type working 2024-05-14 23:15:53 +02:00
mfahampshire 4993c7341c sort of working 2024-05-14 22:37:05 +02:00
mfahampshire c47248fd74 added another check for run command 2024-05-13 20:04:12 +02:00
mfahampshire 182b3fafab * formatting of output
* got rid of stderr printing
* a bunch of info! printing
* gitignore
* creates files in dir/
2024-05-08 00:04:24 +02:00
mfahampshire 3dd4db1030 added to workspace 2024-05-07 18:11:26 +02:00
mfahampshire fdb5f11511 first commit 2024-05-07 18:11:03 +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
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
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
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
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
Sachin Kamath 1ebb0c7daa don't use bigdecimal 2024-03-18 20:28:55 +05:30
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
112 changed files with 5588 additions and 618 deletions
@@ -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
+66 -23
View File
@@ -223,23 +223,24 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
version = "0.6.4"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.4"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
@@ -536,6 +537,14 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "autodoc"
version = "0.1.0"
dependencies = [
"env_logger 0.11.3",
"log",
]
[[package]]
name = "axum"
version = "0.6.20"
@@ -2491,6 +2500,16 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "env_filter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.7.1"
@@ -2514,6 +2533,19 @@ dependencies = [
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime 2.1.0",
"log",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -2547,7 +2579,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.33"
version = "1.1.34"
dependencies = [
"chrono",
"clap 4.4.7",
@@ -3507,7 +3539,7 @@ dependencies = [
"futures-util",
"http 0.2.9",
"hyper 0.14.27",
"rustls 0.21.10",
"rustls 0.21.11",
"tokio",
"tokio-rustls 0.24.1",
]
@@ -3852,6 +3884,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "isahc"
version = "1.7.2"
@@ -4464,9 +4502,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.20"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "loom"
@@ -5024,7 +5062,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.35"
version = "1.1.37"
dependencies = [
"anyhow",
"async-trait",
@@ -5113,6 +5151,7 @@ dependencies = [
"schemars",
"serde",
"tendermint",
"time",
"ts-rs",
]
@@ -5185,7 +5224,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.34"
version = "1.1.35"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -5266,7 +5305,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.33"
version = "1.1.34"
dependencies = [
"bs58 0.5.0",
"clap 4.4.7",
@@ -5683,7 +5722,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.1.33"
version = "1.1.35"
dependencies = [
"anyhow",
"async-trait",
@@ -5756,7 +5795,9 @@ dependencies = [
"nym-validator-client",
"rand 0.7.3",
"serde",
"si-scale",
"thiserror",
"time",
"tokio",
"tokio-stream",
"tokio-tungstenite",
@@ -5976,7 +6017,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.35"
version = "1.1.37"
dependencies = [
"anyhow",
"axum",
@@ -6095,7 +6136,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.33"
version = "1.1.34"
dependencies = [
"addr",
"anyhow",
@@ -6147,7 +6188,7 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "1.1.33"
version = "1.1.34"
dependencies = [
"dirs 4.0.0",
"log",
@@ -6164,7 +6205,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "0.1.0"
version = "1.1.0"
dependencies = [
"anyhow",
"bip39",
@@ -6172,6 +6213,7 @@ dependencies = [
"cargo_metadata",
"celes",
"clap 4.4.7",
"colored",
"cupid",
"humantime-serde",
"ipnetwork 0.16.0",
@@ -6247,6 +6289,7 @@ dependencies = [
"nym-exit-policy",
"nym-http-api-client",
"nym-wireguard-types",
"rand_chacha 0.2.2",
"schemars",
"serde",
"serde_json",
@@ -6433,7 +6476,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.33"
version = "1.1.34"
dependencies = [
"bs58 0.5.0",
"clap 4.4.7",
@@ -8313,7 +8356,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite 0.2.13",
"rustls 0.21.10",
"rustls 0.21.11",
"rustls-native-certs",
"rustls-pemfile",
"serde",
@@ -8697,9 +8740,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.21.10"
version = "0.21.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
dependencies = [
"log",
"ring 0.17.4",
@@ -10096,7 +10139,7 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.10",
"rustls 0.21.11",
"tokio",
]
@@ -10157,7 +10200,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [
"futures-util",
"log",
"rustls 0.21.10",
"rustls 0.21.11",
"rustls-native-certs",
"tokio",
"tokio-rustls 0.24.1",
@@ -10591,7 +10634,7 @@ dependencies = [
"httparse",
"log",
"rand 0.8.5",
"rustls 0.21.10",
"rustls 0.21.11",
"sha1",
"thiserror",
"url",
+2 -1
View File
@@ -90,6 +90,7 @@ members = [
"common/wasm/utils",
"common/wireguard",
"common/wireguard-types",
"documentation/autodoc",
"explorer-api",
"explorer-api/explorer-api-requests",
"explorer-api/explorer-client",
@@ -121,7 +122,7 @@ members = [
"wasm/client",
# "wasm/full-nym-wasm",
"wasm/mix-fetch",
"wasm/node-tester",
"wasm/node-tester", "documentation/autodoc",
]
default-members = [
+1 -1
View File
@@ -1,6 +1,6 @@
[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"
@@ -8,5 +8,7 @@ use nym_client_core::cli_helpers::client_import_credential::{
};
pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> {
import_credential::<CliNativeClient, _>(args).await
import_credential::<CliNativeClient, _>(args).await?;
println!("successfully imported credential!");
Ok(())
}
+1 -1
View File
@@ -1,6 +1,6 @@
[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"
@@ -10,5 +10,7 @@ use nym_client_core::cli_helpers::client_import_credential::{
pub(crate) async fn execute(
args: CommonClientImportCredentialArgs,
) -> Result<(), Socks5ClientError> {
import_credential::<CliSocks5Client, _>(args).await
import_credential::<CliSocks5Client, _>(args).await?;
println!("successfully imported credential!");
Ok(())
}
+1
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,
+3 -3
View File
@@ -201,7 +201,7 @@ where
log::debug!("Setting up gateway");
match setup {
GatewaySetup::MustLoad { gateway_id } => {
log::trace!("GatewaySetup::MustLoad with id: {gateway_id:?}");
log::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}");
use_loaded_gateway_details(key_store, details_store, gateway_id).await
}
GatewaySetup::New {
@@ -209,7 +209,7 @@ where
available_gateways,
wg_tun_address,
} => {
log::trace!("GatewaySetup::New with spec: {specification:?}");
log::debug!("GatewaySetup::New with spec: {specification:?}");
setup_new_gateway(
key_store,
details_store,
@@ -224,7 +224,7 @@ where
gateway_details,
client_keys: managed_keys,
} => {
log::trace!("GatewaySetup::ReuseConnection");
log::debug!("GatewaySetup::ReuseConnection");
Ok(reuse_gateway_connection(
authenticated_ephemeral_client,
*gateway_details,
@@ -16,6 +16,8 @@ thiserror = { workspace = true }
url = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
tokio = { version = "1.24.1", features = ["macros"] }
si-scale = "0.2.2"
time.workspace = true
# internal
nym-bandwidth-controller = { path = "../../bandwidth-controller" }
@@ -79,6 +79,7 @@ impl GatewayConfig {
}
// TODO: this should be refactored into a state machine that keeps track of its authentication state
#[derive(Debug)]
pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
authenticated: bool,
disabled_credentials_mode: bool,
@@ -849,6 +850,7 @@ impl<C, St> GatewayClient<C, St> {
// type alias for an ease of use
pub type InitGatewayClient = GatewayClient<InitOnly>;
#[derive(Debug)]
pub struct InitOnly;
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
@@ -10,9 +10,14 @@ use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::ServerResponse;
use nym_task::TaskClient;
use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
use tungstenite::Message;
#[cfg(unix)]
@@ -48,6 +53,22 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
None
}
// disgusting? absolutely, but does the trick for now
static LAST_LOGGED_BANDWIDTH_TS: AtomicI64 = AtomicI64::new(0);
fn maybe_log_bandwidth(remaining: i64) {
// SAFETY: this value is always populated with valid timestamps
let last =
OffsetDateTime::from_unix_timestamp(LAST_LOGGED_BANDWIDTH_TS.load(Ordering::Relaxed))
.unwrap();
let now = OffsetDateTime::now_utc();
if last + Duration::from_secs(10) < now {
log::info!("remaining bandwidth: {}", bibytes2(remaining as f64));
LAST_LOGGED_BANDWIDTH_TS.store(now.unix_timestamp(), Ordering::Relaxed)
}
}
#[derive(Debug)]
pub(crate) struct PartiallyDelegated {
sink_half: SplitSink<WsConn, Message>,
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
@@ -55,7 +76,10 @@ pub(crate) struct PartiallyDelegated {
}
impl PartiallyDelegated {
fn recover_received_plaintexts(ws_msgs: Vec<Message>, shared_key: &SharedKeys) -> Vec<Vec<u8>> {
fn recover_received_plaintexts(
ws_msgs: Vec<Message>,
shared_key: &SharedKeys,
) -> Result<Vec<Vec<u8>>, GatewayClientError> {
let mut plaintexts = Vec::with_capacity(ws_msgs.len());
for ws_msg in ws_msgs {
match ws_msg {
@@ -73,15 +97,32 @@ impl PartiallyDelegated {
// TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
Message::Text(text) => {
trace!(
"received a text message - probably a response to some previous query! - {}",
text
"received a text message - probably a response to some previous query! - {text}",
);
match ServerResponse::try_from(text)
.map_err(|_| GatewayClientError::MalformedResponse)?
{
ServerResponse::Send {
remaining_bandwidth,
} => maybe_log_bandwidth(remaining_bandwidth),
ServerResponse::Error { message } => {
error!("gateway failure: {message}");
return Err(GatewayClientError::GatewayError(message));
}
other => {
warn!(
"received illegal message of type {} in an authenticated client",
other.name()
)
}
}
continue;
}
_ => continue,
}
}
plaintexts
Ok(plaintexts)
}
fn route_socket_messages(
@@ -89,7 +130,7 @@ impl PartiallyDelegated {
packet_router: &PacketRouter,
shared_key: &SharedKeys,
) -> Result<(), GatewayClientError> {
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key);
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key)?;
packet_router.route_received(plaintexts)
}
@@ -129,7 +170,8 @@ impl PartiallyDelegated {
};
if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) {
log::warn!("Route socket messages failed: {err}");
log::error!("Route socket messages failed: {err}");
break Err(err)
}
}
};
@@ -221,6 +263,7 @@ impl PartiallyDelegated {
// we can either have the stream itself or an option to re-obtain it
// by notifying the future owning it to finish the execution and awaiting the result
// which should be almost immediate (or an invalid state which should never, ever happen)
#[derive(Debug)]
pub(crate) enum SocketState {
Available(Box<WsConn>),
PartiallyDelegated(PartiallyDelegated),
@@ -148,6 +148,10 @@ pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
bail!("the provided free pass request has too long expiry (expiry is set to on {expiration_date})")
}
if expiration_date < now {
bail!("the provided free pass expiry is set in the past!")
}
// issuance start
block_until_coconut_is_available(&client).await?;
@@ -0,0 +1,354 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::fs::OpenOptions;
use clap::Parser;
use comfy_table::Table;
use csv::WriterBuilder;
use log::info;
use nym_mixnet_contract_common::ExecuteMsg;
use nym_mixnet_contract_common::ExecuteMsg::{DelegateToMixnode, UndelegateFromMixnode};
use nym_mixnet_contract_common::PendingEpochEventKind::{Delegate, Undelegate};
use nym_validator_client::nyxd::contract_traits::{NymContractsProvider, PagedMixnetQueryClient};
use nym_validator_client::nyxd::Coin;
use crate::context::SigningClient;
use crate::utils::pretty_coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub memo: Option<String>,
#[clap(
long,
help = "Input csv files with delegation amounts. Format: (mixID, amount(in NYM))"
)]
pub input: String,
#[clap(
long,
help = "An output file path (CSV format) to create or append a log of results to"
)]
pub output: Option<String>,
}
#[derive(Debug)]
pub struct InputFileRow {
pub mix_id: String,
pub amount: Coin,
}
#[derive(Debug)]
pub struct InputFileReader {
pub rows: Vec<InputFileRow>,
}
impl InputFileReader {
pub fn new(path: &str) -> Result<InputFileReader, anyhow::Error> {
let file_contents = fs::read_to_string(path)?;
let mut rows = Vec::new();
let mut mix_id_set = HashSet::new();
for line in file_contents
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
{
let tokens: Vec<_> = line.split(',').collect();
if tokens.len() != 2 {
anyhow::bail!("Incorrect format: {}", line);
}
let mix_id = tokens[0].trim().to_string();
let input_amount = tokens[1]
.trim()
.parse::<u128>()
.map_err(|_| anyhow::anyhow!("'{}' has an invalid amount", line))?;
let micro_nym_amount = input_amount * 1_000_000;
if !mix_id_set.insert(mix_id.clone()) {
anyhow::bail!("Duplicate mix_id found: {}", mix_id);
}
rows.push(InputFileRow {
mix_id,
amount: Coin {
amount: micro_nym_amount,
denom: "unym".to_string(),
},
});
}
Ok(InputFileReader { rows })
}
}
fn write_to_csv(
output_details: Vec<[String; 3]>,
output_file: Option<String>,
) -> Result<(), anyhow::Error> {
if let Some(file_path) = output_file {
// Determine if the file exists and is not empty
let file_exists = fs::metadata(&file_path)
.map(|metadata| metadata.len() > 0)
.unwrap_or(false);
// Open the file for appending or creation
let file = OpenOptions::new()
.append(true)
.create(true)
.open(&file_path)?;
if !file_exists {
let mut wtr = csv::Writer::from_writer(&file);
wtr.write_record(["Operation", "Transaction Hash", "Timestamp"])?;
wtr.flush()?;
}
let mut wtr = WriterBuilder::new()
.has_headers(!file_exists)
.from_writer(file);
// Write the details to the CSV file
for detail in output_details {
wtr.write_record(&detail)?;
}
wtr.flush()?;
info!("All operations saved to output file");
}
Ok(())
}
async fn fetch_delegation_data(
client: &SigningClient,
) -> Result<HashMap<String, Coin>, anyhow::Error> {
let address = client.address();
// Fetch all delegations for the user
let delegations = match client.get_all_delegator_delegations(&address).await {
Ok(delegations) => delegations,
Err(e) => {
anyhow::bail!("Error fetching delegations: {}", e)
}
};
// Build a map to make it easier to handle delegation data
let mut existing_delegation_map: HashMap<String, Coin> = HashMap::new();
let mut pending_delegation_map: HashMap<String, Coin> = HashMap::new();
for delegation in delegations {
existing_delegation_map
.insert(delegation.mix_id.to_string(), Coin::from(delegation.amount));
}
// Look for pending delegate / undelegate events which might be of interest to us
let pending_events = match client.get_all_pending_epoch_events().await {
Ok(events) => events,
Err(e) => {
anyhow::bail!("Error fetching pending epoch events: {}", e);
}
};
for event in pending_events {
match event.event.kind {
// If a pending undelegate tx is found, remove it from delegation map
Undelegate { owner, mix_id, .. } => {
if owner == address.as_ref()
&& existing_delegation_map.get(&mix_id.to_string()).is_some()
{
existing_delegation_map.remove(&mix_id.to_string());
}
}
// If a pending delegation event is found, gather them to consolidate later
Delegate {
owner,
mix_id,
amount,
..
} => {
if owner == address.as_ref() {
let mut amount = Coin::from(amount);
if let Some(pending_record) = pending_delegation_map.get(&mix_id.to_string()) {
amount.amount += pending_record.amount;
}
pending_delegation_map.insert(mix_id.to_string(), amount);
}
}
_ => {}
};
}
// Consolidate pending events into delegation map
for (mix_id, amount) in pending_delegation_map {
existing_delegation_map
.entry(mix_id)
.and_modify(|e| e.amount += amount.amount)
.or_insert(amount);
}
Ok(existing_delegation_map)
}
pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
let records = match InputFileReader::new(&args.input) {
Ok(records) => records,
Err(e) => {
println!("Error reading input file: {}", e);
return;
}
};
let existing_delegation_map = fetch_delegation_data(&client)
.await
.expect("Could not fetch existing delegations");
let mut delegation_table = Table::new();
let mut undelegation_table = Table::new();
let mut delegation_msgs: Vec<(ExecuteMsg, Vec<Coin>)> = Vec::new();
let mut undelegation_msgs: Vec<(ExecuteMsg, Vec<Coin>)> = Vec::new();
delegation_table.set_header(["Mix ID", "Input Amount", "Adjusted Amount"]);
undelegation_table.set_header(["Mix ID"]);
for row in &records.rows {
let input_amount = row.amount.amount;
let existing_delegation_amount = existing_delegation_map
.get(&row.mix_id)
.map_or(0, |coin| coin.amount);
match existing_delegation_amount.cmp(&input_amount) {
Ordering::Equal => continue, // No action needed if amounts are equal
Ordering::Less => {
// Delegate the difference if the existing delegation is less
let difference = Coin {
amount: input_amount - existing_delegation_amount,
denom: row.amount.denom.clone(),
};
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![difference.clone()]));
delegation_table.add_row(&[
row.mix_id.clone(),
pretty_coin(&row.amount),
pretty_coin(&difference),
]);
}
Ordering::Greater => {
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
let coins: Vec<Coin> = vec![];
undelegation_msgs.push((UndelegateFromMixnode { mix_id }, coins));
undelegation_table.add_row(&[row.mix_id.clone()]);
if row.amount.amount > 0 {
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![row.amount.clone()]));
delegation_table.add_row(&[
row.mix_id.clone(),
pretty_coin(&row.amount),
pretty_coin(&row.amount),
]);
}
}
}
}
if delegation_msgs.is_empty() && undelegation_msgs.is_empty() {
println!("Nothing to do. Delegations are up-to-date!");
return;
}
if !undelegation_msgs.is_empty() {
println!("Undelegation records : \n{}\n\n", undelegation_table);
}
if !delegation_msgs.is_empty() {
println!("Delegation records : \n{}\n\n", delegation_table);
}
let ans = inquire::Confirm::new("Do you want to continue with the shown operations?")
.with_default(false)
.with_help_message("You must confirm before the transactions are signed")
.prompt();
if let Err(e) = ans {
info!("Aborting, {}...", e);
return;
}
if let Ok(false) = ans {
info!("Aborting:: User denied proceeding with signing!");
return;
}
let mut output_details: Vec<[String; 3]> = Vec::new();
let now = time::OffsetDateTime::now_utc();
let now = now
.format(&time::format_description::well_known::Rfc3339)
.unwrap();
let mixnet_contract = client
.mixnet_contract_address()
.expect("mixnet contract address is not available");
// Execute all undelegation transactions
if !undelegation_msgs.is_empty() {
let res = client
.execute_multiple(
mixnet_contract,
undelegation_msgs.clone(),
None,
format!(
"Undelegate from {} nodes via nym-cli",
undelegation_msgs.len()
),
)
.await
.expect("Could not undelegate!");
println!(
"Undelegation transaction successful : {}",
res.transaction_hash
);
output_details.push([
"Undelegate".to_string(),
res.transaction_hash.to_string(),
now.clone(),
]);
}
// Execute all delegation delegations
if !delegation_msgs.is_empty() {
let res = client
.execute_multiple(
mixnet_contract,
delegation_msgs,
None,
format!(
"Delegatation to {} nodes via nym-cli",
undelegation_msgs.len()
),
)
.await
.expect("Could not delegate");
println!(
"Delegation transaction successful : {}",
res.transaction_hash
);
output_details.push([
"Delegate".to_string(),
res.transaction_hash.to_string(),
now.clone(),
]);
}
if args.output.is_some() {
if let Err(e) = write_to_csv(output_details, args.output) {
info!("Failed to write to CSV, {}", e);
}
}
}
@@ -6,6 +6,7 @@ use clap::{Args, Subcommand};
pub mod rewards;
pub mod delegate_to_mixnode;
pub mod delegate_to_multiple_mixnodes;
pub mod query_for_delegations;
pub mod undelegate_from_mixnode;
pub mod vesting_delegate_to_mixnode;
@@ -26,6 +27,8 @@ pub enum MixnetDelegatorsCommands {
Rewards(rewards::MixnetDelegatorsReward),
/// Delegate to a mixnode
Delegate(delegate_to_mixnode::Args),
/// Perform bulk delegations from an input file
DelegateMulti(delegate_to_multiple_mixnodes::Args),
/// Undelegate from a mixnode
Undelegate(undelegate_from_mixnode::Args),
/// Delegate to a mixnode with locked tokens
@@ -0,0 +1,48 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
ALTER TABLE coconut_credentials
RENAME TO old_coconut_credentials;
CREATE TABLE coconut_credentials
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-- introduce a way for us to introduce breaking changes in serialization
serialization_revision INTEGER NOT NULL,
-- the best we can do without enums
credential_type TEXT CHECK ( credential_type IN ('BandwidthVoucher', 'FreeBandwidthPass') ) NOT NULL,
credential_data BLOB NOT NULL UNIQUE,
epoch_id INTEGER NOT NULL,
-- this field is only really applicable to free passes
expired BOOLEAN NOT NULL
);
ALTER TABLE credential_usage
RENAME TO old_credential_usage;
-- for bandwidth vouchers there's going to be only a single entry; for freepasses there can be as many as there are gateways
CREATE TABLE credential_usage
(
credential_id INTEGER NOT NULL REFERENCES coconut_credentials (id),
gateway_id_bs58 TEXT NOT NULL,
-- no matter credential type, we can't spend the same credential with the same gateway multiple times
UNIQUE (credential_id, gateway_id_bs58)
);
INSERT INTO coconut_credentials
SELECT *
FROM old_coconut_credentials;
INSERT INTO credential_usage
SELECT *
FROM old_credential_usage;
DROP TABLE old_coconut_credentials;
DROP TABLE old_credential_usage;
@@ -44,7 +44,7 @@ impl CoconutCredentialManager {
r#"
SELECT *
FROM coconut_credentials
WHERE coconut_credentials.credential_type == "FreeBandwidthPass"
WHERE coconut_credentials.credential_type == "FreeBandwidthPass" AND coconut_credentials.expired = false
AND NOT EXISTS (SELECT 1
FROM credential_usage
WHERE credential_usage.credential_id = coconut_credentials.id
@@ -1,6 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::{self, Debug, Formatter};
use crate::backends::memory::CoconutCredentialManager;
use crate::error::StorageError;
use crate::models::{StorableIssuedCredential, StoredIssuedCredential};
@@ -23,6 +25,12 @@ impl Default for EphemeralStorage {
}
}
impl Debug for EphemeralStorage {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "EphemeralStorage")
}
}
#[async_trait]
impl Storage for EphemeralStorage {
type StorageError = StorageError;
+4
View File
@@ -474,6 +474,10 @@ impl TaskClient {
self.mode.set_should_not_signal_on_drop();
}
pub fn disarm(&mut self) {
self.mark_as_success();
}
pub fn send_we_stopped(&mut self, err: SentError) {
if self.mode.is_dummy() {
return;
+2 -2
View File
@@ -205,10 +205,10 @@ impl<'a> TryFrom<&'a DescribedGateway> for Node {
clients_ws_port: self_described.mixnet_websockets.ws_port,
clients_wss_port: self_described.mixnet_websockets.wss_port,
identity_key: identity::PublicKey::from_base58_string(
&self_described.host_information.keys.ed25519_identity,
&self_described.host_information.keys.ed25519,
)?,
sphinx_key: encryption::PublicKey::from_base58_string(
&self_described.host_information.keys.x25519_sphinx,
&self_described.host_information.keys.x25519,
)?,
version: self_described
.build_information
+7
View File
@@ -6,6 +6,7 @@ use futures::{Sink, Stream};
use gloo_net::websocket::futures::WebSocket;
use gloo_net::websocket::{Message, WebSocketError};
use gloo_utils::errors::JsError;
use std::fmt::{self, Formatter};
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -77,6 +78,12 @@ impl Stream for JSWebsocket {
}
}
impl fmt::Debug for JSWebsocket {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "JSWebSocket")
}
}
impl Sink<WsMessage> for JSWebsocket {
type Error = WsError;
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "autodoc"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
env_logger = "0.11.3"
log.workspace = true
+4
View File
@@ -0,0 +1,4 @@
# `autodoc`
Command output documentation generator WIP
@@ -0,0 +1,88 @@
# `nym-api` Binary Commands
These docs are autogenerated by the `autodocs` script.
**TODO add link**
```
Starting nym api...
Usage: nym-api [OPTIONS] <COMMAND>
Commands:
init Initialise a Nym Api instance with persistent config.toml file
run Run the Nym Api with provided configuration optionally overriding set parameters
build-info Show build information of this binary
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE> Path pointing to an env file that configures the Nym API
-h, --help Print help
-V, --version Print version
```
### `init`
```
Starting nym api...
Initialise a Nym Api instance with persistent config.toml file
Usage: nym-api init [OPTIONS]
Options:
--id <ID>
Id of the nym-api we want to initialise. if unspecified, a default value will be used. default: "default" [default: default]
-m, --enable-monitor
Specifies whether network monitoring is enabled on this API default: false
-r, --enable-rewarding
Specifies whether network rewarding is enabled on this API default: false
--nyxd-validator <NYXD_VALIDATOR>
Endpoint to nyxd instance used for contract information. default: http://localhost:26657
--mnemonic <MNEMONIC>
Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions default: None
--enable-zk-nym
Flag to indicate whether credential signer authority is enabled on this API default: false
--announce-address <ANNOUNCE_ADDRESS>
Announced address that is going to be put in the DKG contract where zk-nym clients will connect to obtain their credentials default: None
--monitor-credentials-mode
Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
-h, --help
Print help
```
### `run`
```
Starting nym api...
Run the Nym Api with provided configuration optionally overriding set parameters
Usage: nym-api run [OPTIONS]
Options:
--id <ID>
Id of the nym-api we want to run.if unspecified, a default value will be used. default: "default" [default: default]
-m, --enable-monitor <ENABLE_MONITOR>
Specifies whether network monitoring is enabled on this API default: None - config value will be used instead [possible values: true, false]
-r, --enable-rewarding <ENABLE_REWARDING>
Specifies whether network rewarding is enabled on this API default: None - config value will be used instead [possible values: true, false]
--nyxd-validator <NYXD_VALIDATOR>
Endpoint to nyxd instance used for contract information. default: None - config value will be used instead
--mnemonic <MNEMONIC>
Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions default: None - config value will be used instead
--enable-zk-nym <ENABLE_ZK_NYM>
Flag to indicate whether coconut signer authority is enabled on this API default: None - config value will be used instead [possible values: true, false]
--announce-address <ANNOUNCE_ADDRESS>
Announced address that is going to be put in the DKG contract where zk-nym clients will connect to obtain their credentials default: None - config value will be used instead
--monitor-credentials-mode <MONITOR_CREDENTIALS_MODE>
Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement default: None - config value will be used instead [possible values: true, false]
-h, --help
Print help
```
### `build-info`
```
Starting nym api...
Show build information of this binary
Usage: nym-api build-info [OPTIONS]
Options:
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,721 @@
# `nym-client` Binary Commands
These docs are autogenerated by the `autodocs` script.
**TODO add link**
```
Implementation of the Nym Client
Usage: nym-client [OPTIONS] <COMMAND>
Commands:
init Initialise a Nym client. Do this first!
run Run the Nym client with provided configuration client optionally overriding set parameters
import-credential Import a pre-generated credential
list-gateways List all registered with gateways
add-gateway Add new gateway to this client
switch-gateway Change the currently active gateway. Note that you must have already registered with the new gateway!
build-info Show build information of this binary
completions Generate shell completions
generate-fig-spec Generate Fig specification
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE> Path pointing to an env file that configures the client
--no-banner Flag used for disabling the printed banner in tty
-h, --help Print help
-V, --version Print version
```
### `init`
```
Initialise a Nym client. Do this first!
Usage: nym-client init [OPTIONS] --id <ID>
Options:
--id <ID>
Id of client we want to create config for
--gateway <GATEWAY>
Id of the gateway we are going to connect to
--force-tls-gateway
Specifies whether the client will attempt to enforce tls connection to the desired gateway
--latency-based-selection
Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly
--nym-apis <NYM_APIS>
Comma separated list of rest endpoints of the API validators
--disable-socket <DISABLE_SOCKET>
Whether to not start the websocket [possible values: true, false]
-p, --port <PORT>
Port for the socket (if applicable) to listen on in all subsequent runs
--host <HOST>
Ip for the socket (if applicable) to listen for requests
-o, --output <OUTPUT>
[default: text] [possible values: text, json]
-h, --help
Print help
```
### `run`
```
Run the Nym client with provided configuration client optionally overriding set parameters
Usage: nym-client run [OPTIONS] --id <ID>
Options:
--id <ID>
Id of client we want to create config for
--gateway <GATEWAY>
Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened
--nym-apis <NYM_APIS>
Comma separated list of rest endpoints of the API validators
--disable-socket <DISABLE_SOCKET>
Whether to not start the websocket [possible values: true, false]
-p, --port <PORT>
Port for the socket to listen on
--host <HOST>
Ip for the socket (if applicable) to listen for requests
-h, --help
Print help
```
### `import-credential`
```
Import a pre-generated credential
Usage: nym-client import-credential --id <ID> <--credential-data <CREDENTIAL_DATA>|--credential-path <CREDENTIAL_PATH>>
Options:
--id <ID>
Id of client that is going to import the credential
--credential-data <CREDENTIAL_DATA>
Explicitly provide the encoded credential data (as base58)
--credential-path <CREDENTIAL_PATH>
Specifies the path to file containing binary credential data
-h, --help
Print help
```
### `list-gateways`
```
List all registered with gateways
Usage: nym-client list-gateways [OPTIONS] --id <ID>
Options:
--id <ID> Id of client we want to list gateways for
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
### `switch-gateway`
```
Change the currently active gateway. Note that you must have already registered with the new gateway!
Usage: nym-client switch-gateway --id <ID> --gateway-id <GATEWAY_ID>
Options:
--id <ID> Id of client we want to list gateways for
--gateway-id <GATEWAY_ID> Id of the gateway we want to switch to
-h, --help Print help
```
### `build-info`
```
Show build information of this binary
Usage: nym-client build-info [OPTIONS]
Options:
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
Example output:
```
Binary Name: nym-client
Build Timestamp: 2024-05-07T13:11:43.523733081Z
Build Version: 1.1.34
Commit SHA: 7d233a4a2fc697a2f97e504db6a3765cc9f2dd49
Commit Date: 2024-04-25T11:27:18.000000000+02:00
Commit Branch: max/auto-docs
rustc Version: 1.78.0
rustc Channel: stable
cargo Profile: release
```
### `completions`
```
Generate shell completions
Usage: nym-client completions <SHELL>
Arguments:
<SHELL> [possible values: bash, elvish, fish, power-shell, zsh]
Options:
-h, --help Print help
```
### `generate-fig-spec`
```
Generate Fig specification
Usage: nym-client generate-fig-spec
Options:
-h, --help Print help
```
Example output:
```
const completion: Fig.Spec = {
name: "nym-native-client",
description: "Implementation of the Nym Client",
subcommands: [
{
name: "init",
description: "Initialise a Nym client. Do this first!",
options: [
{
name: "--id",
description: "Id of client we want to create config for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway",
description: "Id of the gateway we are going to connect to",
isRepeatable: true,
args: {
name: "gateway",
isOptional: true,
},
},
{
name: "--nyxd-urls",
description: "Comma separated list of rest endpoints of the nyxd validators",
hidden: true,
isRepeatable: true,
args: {
name: "nyxd_urls",
isOptional: true,
},
},
{
name: "--nym-apis",
description: "Comma separated list of rest endpoints of the API validators",
isRepeatable: true,
args: {
name: "nym_apis",
isOptional: true,
},
},
{
name: "--custom-mixnet",
description: "Path to .json file containing custom network specification",
hidden: true,
isRepeatable: true,
args: {
name: "custom_mixnet",
isOptional: true,
template: "filepaths",
},
},
{
name: "--enabled-credentials-mode",
description: "Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement",
hidden: true,
isRepeatable: true,
args: {
name: "enabled_credentials_mode",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: "--disable-socket",
description: "Whether to not start the websocket",
isRepeatable: true,
args: {
name: "disable_socket",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: ["-p", "--port"],
description: "Port for the socket (if applicable) to listen on in all subsequent runs",
isRepeatable: true,
args: {
name: "port",
isOptional: true,
},
},
{
name: "--host",
description: "Ip for the socket (if applicable) to listen for requests",
isRepeatable: true,
args: {
name: "host",
isOptional: true,
},
},
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: "--force-tls-gateway",
description: "Specifies whether the client will attempt to enforce tls connection to the desired gateway",
},
{
name: "--latency-based-selection",
description: "Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly",
exclusiveOn: [
"--gateway",
],
},
{
name: "--fastmode",
description: "Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init",
},
{
name: "--no-cover",
description: "Disable loop cover traffic and the Poisson rate limiter (for debugging only)",
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "run",
description: "Run the Nym client with provided configuration client optionally overriding set parameters",
options: [
{
name: "--id",
description: "Id of client we want to create config for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway",
description: "Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened",
isRepeatable: true,
args: {
name: "gateway",
isOptional: true,
},
},
{
name: "--nyxd-urls",
description: "Comma separated list of rest endpoints of the nyxd validators",
hidden: true,
isRepeatable: true,
args: {
name: "nyxd_urls",
isOptional: true,
},
},
{
name: "--nym-apis",
description: "Comma separated list of rest endpoints of the API validators",
isRepeatable: true,
args: {
name: "nym_apis",
isOptional: true,
},
},
{
name: "--custom-mixnet",
description: "Path to .json file containing custom network specification",
hidden: true,
isRepeatable: true,
args: {
name: "custom_mixnet",
isOptional: true,
template: "filepaths",
},
},
{
name: "--enabled-credentials-mode",
description: "Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement",
hidden: true,
isRepeatable: true,
args: {
name: "enabled_credentials_mode",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: "--disable-socket",
description: "Whether to not start the websocket",
isRepeatable: true,
args: {
name: "disable_socket",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: ["-p", "--port"],
description: "Port for the socket to listen on",
isRepeatable: true,
args: {
name: "port",
isOptional: true,
},
},
{
name: "--host",
description: "Ip for the socket (if applicable) to listen for requests",
isRepeatable: true,
args: {
name: "host",
isOptional: true,
},
},
{
name: "--fastmode",
description: "Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init",
},
{
name: "--no-cover",
description: "Disable loop cover traffic and the Poisson rate limiter (for debugging only)",
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "import-credential",
description: "Import a pre-generated credential",
options: [
{
name: "--id",
description: "Id of client that is going to import the credential",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--credential-data",
description: "Explicitly provide the encoded credential data (as base58)",
isRepeatable: true,
args: {
name: "credential_data",
isOptional: true,
},
},
{
name: "--credential-path",
description: "Specifies the path to file containing binary credential data",
isRepeatable: true,
args: {
name: "credential_path",
isOptional: true,
template: "filepaths",
},
},
{
name: "--version",
hidden: true,
isRepeatable: true,
args: {
name: "version",
isOptional: true,
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "list-gateways",
description: "List all registered with gateways",
options: [
{
name: "--id",
description: "Id of client we want to list gateways for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "add-gateway",
description: "Add new gateway to this client",
options: [
{
name: "--id",
description: "Id of client we want to add gateway for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway-id",
description: "Explicitly specify id of the gateway to register with. If unspecified, a random gateway will be chosen instead",
isRepeatable: true,
args: {
name: "gateway_id",
isOptional: true,
},
},
{
name: "--nym-apis",
description: "Comma separated list of rest endpoints of the API validators",
isRepeatable: true,
args: {
name: "nym_apis",
isOptional: true,
},
},
{
name: "--custom-mixnet",
description: "Path to .json file containing custom network specification",
hidden: true,
isRepeatable: true,
args: {
name: "custom_mixnet",
isOptional: true,
template: "filepaths",
},
},
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: "--force-tls-gateway",
description: "Specifies whether the client will attempt to enforce tls connection to the desired gateway",
},
{
name: "--latency-based-selection",
description: "Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly",
exclusiveOn: [
"--gateway-id",
],
},
{
name: "--set-active",
description: "Specify whether this new gateway should be set as the active one",
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "switch-gateway",
description: "Change the currently active gateway. Note that you must have already registered with the new gateway!",
options: [
{
name: "--id",
description: "Id of client we want to list gateways for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway-id",
description: "Id of the gateway we want to switch to",
isRepeatable: true,
args: {
name: "gateway_id",
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "build-info",
description: "Show build information of this binary",
options: [
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "completions",
description: "Generate shell completions",
options: [
{
name: ["-h", "--help"],
description: "Print help",
},
],
args: {
name: "shell",
suggestions: [
"bash",
"elvish",
"fish",
"power-shell",
"zsh",
],
},
},
{
name: "generate-fig-spec",
description: "Generate Fig specification",
options: [
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "help",
description: "Print this message or the help of the given subcommand(s)",
subcommands: [
{
name: "init",
description: "Initialise a Nym client. Do this first!",
},
{
name: "run",
description: "Run the Nym client with provided configuration client optionally overriding set parameters",
},
{
name: "import-credential",
description: "Import a pre-generated credential",
},
{
name: "list-gateways",
description: "List all registered with gateways",
},
{
name: "add-gateway",
description: "Add new gateway to this client",
},
{
name: "switch-gateway",
description: "Change the currently active gateway. Note that you must have already registered with the new gateway!",
},
{
name: "build-info",
description: "Show build information of this binary",
},
{
name: "completions",
description: "Generate shell completions",
},
{
name: "generate-fig-spec",
description: "Generate Fig specification",
},
{
name: "help",
description: "Print this message or the help of the given subcommand(s)",
},
],
},
],
options: [
{
name: ["-c", "--config-env-file"],
description: "Path pointing to an env file that configures the client",
isRepeatable: true,
args: {
name: "config_env_file",
isOptional: true,
template: "filepaths",
},
},
{
name: "--no-banner",
description: "Flag used for disabling the printed banner in tty",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
};
export default completion;
```
@@ -0,0 +1,227 @@
# `nym-node` Binary Commands
These docs are autogenerated by the `autodocs` script.
**TODO add link**
```
Usage: nym-node [OPTIONS] <COMMAND>
Commands:
build-info Show build information of this binary
bonding-information Show bonding information of this node depending on its currently selected mode
node-details Show details of this node
migrate Attempt to migrate an existing mixnode or gateway into a nym-node
run Start this nym-node
sign Use identity key of this node to sign provided message
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file that configures the nym-node and overrides any preconfigured values [env: NYMNODE_CONFIG_ENV_FILE_ARG=]
--no-banner
Flag used for disabling the printed banner in tty [env: NYMNODE_NO_BANNER=]
-h, --help
Print help
-V, --version
Print version
```
### `build-info`
```
Show build information of this binary
Usage: nym-node build-info [OPTIONS]
Options:
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
### `bonding-information`
```
Show bonding information of this node depending on its currently selected mode
Usage: nym-node bonding-information [OPTIONS]
Options:
--id <ID> Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
--config-file <CONFIG_FILE> Path to a configuration file of this node [env: NYMNODE_CONFIG=]
--mode <MODE> [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway]
-o, --output <OUTPUT> Specify the output format of the bonding information (`text` or `json`) [default: text] [possible values: text, json]
-h, --help Print help
```
### `node-details`
```
Show details of this node
Usage: nym-node node-details [OPTIONS]
Options:
--id <ID> Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
--config-file <CONFIG_FILE> Path to a configuration file of this node [env: NYMNODE_CONFIG=]
-o, --output <OUTPUT> Specify the output format of the node details (`text` or `json`) [default: text] [possible values: text, json]
-h, --help Print help
```
### `migrate`
```
Attempt to migrate an existing mixnode or gateway into a nym-node
Usage: nym-node migrate [OPTIONS] <--id <ID>|--config-file <CONFIG_FILE>> <NODE_TYPE>
Arguments:
<NODE_TYPE> Type of node (mixnode or gateway) to migrate into a nym-node [possible values: mixnode, gateway]
Options:
--id <ID>
Id of the node that's going to get migrated
--config-file <CONFIG_FILE>
Path to a configuration file of the node that's going to get migrated
--preserve-id
Specify whether to preserve id of the imported node
--public-ips <PUBLIC_IPS>
Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. In nearly all circumstances, it's going to be identical to the address you're going to use for bonding [env: NYMNODE_PUBLIC_IPS=]
--hostname <HOSTNAME>
Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=]
--location <LOCATION>
Optional **physical** location of this node's server. Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. 'PL'), three-letter alpha3 (e.g. 'POL') or three-digit numeric-3 (e.g. '616') can be provided [env: NYMNODE_LOCATION=]
--http-bind-address <HTTP_BIND_ADDRESS>
Socket address this node will use for binding its http API. default: `0.0.0.0:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=]
--landing-page-assets-path <LANDING_PAGE_ASSETS_PATH>
Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=]
--http-access-token <HTTP_ACCESS_TOKEN>
An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=]
--expose-system-info <EXPOSE_SYSTEM_INFO>
Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false]
--expose-system-hardware <EXPOSE_SYSTEM_HARDWARE>
Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false]
--expose-crypto-hardware <EXPOSE_CRYPTO_HARDWARE>
Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false]
--mixnet-bind-address <MIXNET_BIND_ADDRESS>
Address this node will bind to for listening for mixnet packets default: `0.0.0.0:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=]
--nym-api-urls <NYM_API_URLS>
Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=]
--nyxd-urls <NYXD_URLS>
Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=]
--wireguard-enabled <WIREGUARD_ENABLED>
Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false]
--wireguard-bind-address <WIREGUARD_BIND_ADDRESS>
Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=]
--wireguard-private-network-ip <WIREGUARD_PRIVATE_NETWORK_IP>
Ip address of the private wireguard network. default: `10.1.0.0` [env: NYMNODE_WG_IP_NETWORK=]
--wireguard-announced-port <WIREGUARD_ANNOUNCED_PORT>
Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=]
--wireguard-private-network-prefix <WIREGUARD_PRIVATE_NETWORK_PREFIX>
The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=]
--verloc-bind-address <VERLOC_BIND_ADDRESS>
Socket address this node will use for binding its verloc API. default: `0.0.0.0:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=]
--entry-bind-address <ENTRY_BIND_ADDRESS>
Socket address this node will use for binding its client websocket API. default: `0.0.0.0:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=]
--announce-ws-port <ANNOUNCE_WS_PORT>
Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=]
--announce-wss-port <ANNOUNCE_WSS_PORT>
If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=]
--enforce-zk-nyms <ENFORCE_ZK_NYMS>
Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible values: true, false]
--mnemonic <MNEMONIC>
Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=]
--upstream-exit-policy-url <UPSTREAM_EXIT_POLICY_URL>
Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=]
--open-proxy <OPEN_PROXY>
Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, false]
-h, --help
Print help
```
### `run`
```
Start this nym-node
Usage: nym-node run [OPTIONS]
Options:
--id <ID>
Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
--config-file <CONFIG_FILE>
Path to a configuration file of this node [env: NYMNODE_CONFIG=]
--deny-init
Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist [env: NYMNODE_DENY_INIT=]
--init-only
If this is a brand new nym-node, specify whether it should only be initialised without actually running the subprocesses [env: NYMNODE_INIT_ONLY=]
--mode <MODE>
Specifies the current mode of this nym-node [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway]
-w, --write-changes
If this node has been initialised before, specify whether to write any new changes to the config file [env: NYMNODE_WRITE_CONFIG_CHANGES=]
--bonding-information-output <BONDING_INFORMATION_OUTPUT>
Specify output file for bonding information of this nym-node, i.e. its encoded keys. NOTE: the required bonding information is still a subject to change and this argument should be treated only as a preview of future features [env: NYMNODE_BONDING_INFORMATION_OUTPUT=]
-o, --output <OUTPUT>
Specify the output format of the bonding information (`text` or `json`) [env: NYMNODE_OUTPUT=] [default: text] [possible values: text, json]
--public-ips <PUBLIC_IPS>
Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. In nearly all circumstances, it's going to be identical to the address you're going to use for bonding [env: NYMNODE_PUBLIC_IPS=]
--hostname <HOSTNAME>
Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=]
--location <LOCATION>
Optional **physical** location of this node's server. Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. 'PL'), three-letter alpha3 (e.g. 'POL') or three-digit numeric-3 (e.g. '616') can be provided [env: NYMNODE_LOCATION=]
--http-bind-address <HTTP_BIND_ADDRESS>
Socket address this node will use for binding its http API. default: `0.0.0.0:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=]
--landing-page-assets-path <LANDING_PAGE_ASSETS_PATH>
Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=]
--http-access-token <HTTP_ACCESS_TOKEN>
An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=]
--expose-system-info <EXPOSE_SYSTEM_INFO>
Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false]
--expose-system-hardware <EXPOSE_SYSTEM_HARDWARE>
Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false]
--expose-crypto-hardware <EXPOSE_CRYPTO_HARDWARE>
Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false]
--mixnet-bind-address <MIXNET_BIND_ADDRESS>
Address this node will bind to for listening for mixnet packets default: `0.0.0.0:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=]
--nym-api-urls <NYM_API_URLS>
Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=]
--nyxd-urls <NYXD_URLS>
Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=]
--wireguard-enabled <WIREGUARD_ENABLED>
Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false]
--wireguard-bind-address <WIREGUARD_BIND_ADDRESS>
Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=]
--wireguard-private-network-ip <WIREGUARD_PRIVATE_NETWORK_IP>
Ip address of the private wireguard network. default: `10.1.0.0` [env: NYMNODE_WG_IP_NETWORK=]
--wireguard-announced-port <WIREGUARD_ANNOUNCED_PORT>
Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=]
--wireguard-private-network-prefix <WIREGUARD_PRIVATE_NETWORK_PREFIX>
The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=]
--verloc-bind-address <VERLOC_BIND_ADDRESS>
Socket address this node will use for binding its verloc API. default: `0.0.0.0:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=]
--entry-bind-address <ENTRY_BIND_ADDRESS>
Socket address this node will use for binding its client websocket API. default: `0.0.0.0:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=]
--announce-ws-port <ANNOUNCE_WS_PORT>
Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=]
--announce-wss-port <ANNOUNCE_WSS_PORT>
If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=]
--enforce-zk-nyms <ENFORCE_ZK_NYMS>
Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible values: true, false]
--mnemonic <MNEMONIC>
Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=]
--upstream-exit-policy-url <UPSTREAM_EXIT_POLICY_URL>
Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=]
--open-proxy <OPEN_PROXY>
Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, false]
-h, --help
Print help
```
### `sign`
```
Use identity key of this node to sign provided message
Usage: nym-node sign [OPTIONS] <--text <TEXT>|--contract-msg <CONTRACT_MSG>>
Options:
--id <ID> Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
--config-file <CONFIG_FILE> Path to a configuration file of this node [env: NYMNODE_CONFIG=]
--text <TEXT> Signs an arbitrary piece of text with your identity key
--contract-msg <CONTRACT_MSG> Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
@@ -0,0 +1,790 @@
# `nym-socks5-client` Binary Commands
These docs are autogenerated by the `autodocs` script.
**TODO add link**
```
A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address
Usage: nym-socks5-client [OPTIONS] <COMMAND>
Commands:
init Initialise a Nym client. Do this first!
run Run the Nym client with provided configuration client optionally overriding set parameters
import-credential Import a pre-generated credential
list-gateways List all registered with gateways
add-gateway Add new gateway to this client
switch-gateway Change the currently active gateway. Note that you must have already registered with the new gateway!
build-info Show build information of this binary
completions Generate shell completions
generate-fig-spec Generate Fig specification
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE> Path pointing to an env file that configures the client
--no-banner Flag used for disabling the printed banner in tty
-h, --help Print help
-V, --version Print version
```
### `init`
```
Initialise a Nym client. Do this first!
Usage: nym-socks5-client init [OPTIONS] --id <ID> --provider <PROVIDER>
Options:
--id <ID>
Id of client we want to create config for
--gateway <GATEWAY>
Id of the gateway we are going to connect to
--force-tls-gateway
Specifies whether the client will attempt to enforce tls connection to the desired gateway
--latency-based-selection
Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly
--nym-apis <NYM_APIS>
Comma separated list of rest endpoints of the API validators
--provider <PROVIDER>
Address of the socks5 provider to send messages to
--use-reply-surbs <USE_REPLY_SURBS>
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 slower and consume nearly double the bandwidth as it will require sending reply SURBs.
Note that some service providers might not support this.
[possible values: true, false]
-p, --port <PORT>
Port for the socket to listen on in all subsequent runs
--host <HOST>
The custom host on which the socks5 client will be listening for requests
-o, --output <OUTPUT>
[default: text]
[possible values: text, json]
-h, --help
Print help (see a summary with '-h')
```
### `run`
```
Run the Nym client with provided configuration client optionally overriding set parameters
Usage: nym-socks5-client run [OPTIONS] --id <ID>
Options:
--id <ID>
Id of client we want to create config for
--gateway <GATEWAY>
Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened
--nym-apis <NYM_APIS>
Comma separated list of rest endpoints of the API validators
--use-anonymous-replies <USE_ANONYMOUS_REPLIES>
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 slower and consume nearly double the bandwidth as it will require sending reply SURBs.
Note that some service providers might not support this.
[possible values: true, false]
--provider <PROVIDER>
Address of the socks5 provider to send messages to
-p, --port <PORT>
Port for the socket to listen on
--host <HOST>
The custom host on which the socks5 client will be listening for requests
-h, --help
Print help (see a summary with '-h')
```
### `import-credential`
```
Import a pre-generated credential
Usage: nym-socks5-client import-credential --id <ID> <--credential-data <CREDENTIAL_DATA>|--credential-path <CREDENTIAL_PATH>>
Options:
--id <ID>
Id of client that is going to import the credential
--credential-data <CREDENTIAL_DATA>
Explicitly provide the encoded credential data (as base58)
--credential-path <CREDENTIAL_PATH>
Specifies the path to file containing binary credential data
-h, --help
Print help
```
### `list-gateways`
```
List all registered with gateways
Usage: nym-socks5-client list-gateways [OPTIONS] --id <ID>
Options:
--id <ID> Id of client we want to list gateways for
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
### `add-gateway`
```
Add new gateway to this client
Usage: nym-socks5-client add-gateway [OPTIONS] --id <ID>
Options:
--id <ID> Id of client we want to add gateway for
--gateway-id <GATEWAY_ID> Explicitly specify id of the gateway to register with. If unspecified, a random gateway will be chosen instead
--force-tls-gateway Specifies whether the client will attempt to enforce tls connection to the desired gateway
--latency-based-selection Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly
--set-active Specify whether this new gateway should be set as the active one
--nym-apis <NYM_APIS> Comma separated list of rest endpoints of the API validators
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
### `build-info`
```
Show build information of this binary
Usage: nym-socks5-client build-info [OPTIONS]
Options:
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
Example output:
```
Binary Name: nym-socks5-client
Build Timestamp: 2024-05-07T13:11:43.523733081Z
Build Version: 1.1.34
Commit SHA: 7d233a4a2fc697a2f97e504db6a3765cc9f2dd49
Commit Date: 2024-04-25T11:27:18.000000000+02:00
Commit Branch: max/auto-docs
rustc Version: 1.78.0
rustc Channel: stable
cargo Profile: release
```
### `completions`
```
Generate shell completions
Usage: nym-socks5-client completions <SHELL>
Arguments:
<SHELL> [possible values: bash, elvish, fish, power-shell, zsh]
Options:
-h, --help Print help
```
### `generate-fig-spec`
```
Generate Fig specification
Usage: nym-socks5-client generate-fig-spec
Options:
-h, --help Print help
```
Example output:
```
const completion: Fig.Spec = {
name: "nym-socks5-client",
description: "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address",
subcommands: [
{
name: "init",
description: "Initialise a Nym client. Do this first!",
options: [
{
name: "--id",
description: "Id of client we want to create config for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway",
description: "Id of the gateway we are going to connect to",
isRepeatable: true,
args: {
name: "gateway",
isOptional: true,
},
},
{
name: "--nyxd-urls",
description: "Comma separated list of rest endpoints of the nyxd validators",
hidden: true,
isRepeatable: true,
args: {
name: "nyxd_urls",
isOptional: true,
},
},
{
name: "--nym-apis",
description: "Comma separated list of rest endpoints of the API validators",
isRepeatable: true,
args: {
name: "nym_apis",
isOptional: true,
},
},
{
name: "--custom-mixnet",
description: "Path to .json file containing custom network specification",
hidden: true,
isRepeatable: true,
args: {
name: "custom_mixnet",
isOptional: true,
template: "filepaths",
},
},
{
name: "--enabled-credentials-mode",
description: "Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement",
hidden: true,
isRepeatable: true,
args: {
name: "enabled_credentials_mode",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: "--provider",
description: "Address of the socks5 provider to send messages to",
isRepeatable: true,
args: {
name: "provider",
},
},
{
name: "--use-reply-surbs",
description: "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 slower and consume nearly double the bandwidth as it will require sending reply SURBs",
isRepeatable: true,
args: {
name: "use_reply_surbs",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: ["-p", "--port"],
description: "Port for the socket to listen on in all subsequent runs",
isRepeatable: true,
args: {
name: "port",
isOptional: true,
},
},
{
name: "--host",
description: "The custom host on which the socks5 client will be listening for requests",
isRepeatable: true,
args: {
name: "host",
isOptional: true,
},
},
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: "--force-tls-gateway",
description: "Specifies whether the client will attempt to enforce tls connection to the desired gateway",
},
{
name: "--latency-based-selection",
description: "Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly",
exclusiveOn: [
"--gateway",
],
},
{
name: "--fastmode",
description: "Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init",
},
{
name: "--no-cover",
description: "Disable loop cover traffic and the Poisson rate limiter (for debugging only)",
},
{
name: ["-h", "--help"],
description: "Print help (see more with '--help')",
},
],
},
{
name: "run",
description: "Run the Nym client with provided configuration client optionally overriding set parameters",
options: [
{
name: "--id",
description: "Id of client we want to create config for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway",
description: "Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened",
isRepeatable: true,
args: {
name: "gateway",
isOptional: true,
},
},
{
name: "--nyxd-urls",
description: "Comma separated list of rest endpoints of the nyxd validators",
hidden: true,
isRepeatable: true,
args: {
name: "nyxd_urls",
isOptional: true,
},
},
{
name: "--nym-apis",
description: "Comma separated list of rest endpoints of the API validators",
isRepeatable: true,
args: {
name: "nym_apis",
isOptional: true,
},
},
{
name: "--custom-mixnet",
description: "Path to .json file containing custom network specification",
hidden: true,
isRepeatable: true,
args: {
name: "custom_mixnet",
isOptional: true,
template: "filepaths",
},
},
{
name: "--enabled-credentials-mode",
description: "Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement",
hidden: true,
isRepeatable: true,
args: {
name: "enabled_credentials_mode",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: "--use-anonymous-replies",
description: "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 slower and consume nearly double the bandwidth as it will require sending reply SURBs",
isRepeatable: true,
args: {
name: "use_anonymous_replies",
isOptional: true,
suggestions: [
"true",
"false",
],
},
},
{
name: "--provider",
description: "Address of the socks5 provider to send messages to",
isRepeatable: true,
args: {
name: "provider",
isOptional: true,
},
},
{
name: ["-p", "--port"],
description: "Port for the socket to listen on",
isRepeatable: true,
args: {
name: "port",
isOptional: true,
},
},
{
name: "--host",
description: "The custom host on which the socks5 client will be listening for requests",
isRepeatable: true,
args: {
name: "host",
isOptional: true,
},
},
{
name: "--geo-routing",
description: "Set geo-aware mixnode selection when sending mixnet traffic, for experiments only",
hidden: true,
isRepeatable: true,
args: {
name: "geo_routing",
isOptional: true,
},
},
{
name: "--fastmode",
description: "Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init",
},
{
name: "--no-cover",
description: "Disable loop cover traffic and the Poisson rate limiter (for debugging only)",
},
{
name: "--medium-toggle",
description: "Enable medium mixnet traffic, for experiments only. This includes things like disabling cover traffic, no per hop delays, etc",
},
{
name: "--outfox",
},
{
name: ["-h", "--help"],
description: "Print help (see more with '--help')",
},
],
},
{
name: "import-credential",
description: "Import a pre-generated credential",
options: [
{
name: "--id",
description: "Id of client that is going to import the credential",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--credential-data",
description: "Explicitly provide the encoded credential data (as base58)",
isRepeatable: true,
args: {
name: "credential_data",
isOptional: true,
},
},
{
name: "--credential-path",
description: "Specifies the path to file containing binary credential data",
isRepeatable: true,
args: {
name: "credential_path",
isOptional: true,
template: "filepaths",
},
},
{
name: "--version",
hidden: true,
isRepeatable: true,
args: {
name: "version",
isOptional: true,
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "list-gateways",
description: "List all registered with gateways",
options: [
{
name: "--id",
description: "Id of client we want to list gateways for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "add-gateway",
description: "Add new gateway to this client",
options: [
{
name: "--id",
description: "Id of client we want to add gateway for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway-id",
description: "Explicitly specify id of the gateway to register with. If unspecified, a random gateway will be chosen instead",
isRepeatable: true,
args: {
name: "gateway_id",
isOptional: true,
},
},
{
name: "--nym-apis",
description: "Comma separated list of rest endpoints of the API validators",
isRepeatable: true,
args: {
name: "nym_apis",
isOptional: true,
},
},
{
name: "--custom-mixnet",
description: "Path to .json file containing custom network specification",
hidden: true,
isRepeatable: true,
args: {
name: "custom_mixnet",
isOptional: true,
template: "filepaths",
},
},
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: "--force-tls-gateway",
description: "Specifies whether the client will attempt to enforce tls connection to the desired gateway",
},
{
name: "--latency-based-selection",
description: "Specifies whether the new gateway should be determined based by latency as opposed to being chosen uniformly",
exclusiveOn: [
"--gateway-id",
],
},
{
name: "--set-active",
description: "Specify whether this new gateway should be set as the active one",
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "switch-gateway",
description: "Change the currently active gateway. Note that you must have already registered with the new gateway!",
options: [
{
name: "--id",
description: "Id of client we want to list gateways for",
isRepeatable: true,
args: {
name: "id",
},
},
{
name: "--gateway-id",
description: "Id of the gateway we want to switch to",
isRepeatable: true,
args: {
name: "gateway_id",
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "build-info",
description: "Show build information of this binary",
options: [
{
name: ["-o", "--output"],
isRepeatable: true,
args: {
name: "output",
isOptional: true,
suggestions: [
"text",
"json",
],
},
},
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "completions",
description: "Generate shell completions",
options: [
{
name: ["-h", "--help"],
description: "Print help",
},
],
args: {
name: "shell",
suggestions: [
"bash",
"elvish",
"fish",
"power-shell",
"zsh",
],
},
},
{
name: "generate-fig-spec",
description: "Generate Fig specification",
options: [
{
name: ["-h", "--help"],
description: "Print help",
},
],
},
{
name: "help",
description: "Print this message or the help of the given subcommand(s)",
subcommands: [
{
name: "init",
description: "Initialise a Nym client. Do this first!",
},
{
name: "run",
description: "Run the Nym client with provided configuration client optionally overriding set parameters",
},
{
name: "import-credential",
description: "Import a pre-generated credential",
},
{
name: "list-gateways",
description: "List all registered with gateways",
},
{
name: "add-gateway",
description: "Add new gateway to this client",
},
{
name: "switch-gateway",
description: "Change the currently active gateway. Note that you must have already registered with the new gateway!",
},
{
name: "build-info",
description: "Show build information of this binary",
},
{
name: "completions",
description: "Generate shell completions",
},
{
name: "generate-fig-spec",
description: "Generate Fig specification",
},
{
name: "help",
description: "Print this message or the help of the given subcommand(s)",
},
],
},
],
options: [
{
name: ["-c", "--config-env-file"],
description: "Path pointing to an env file that configures the client",
isRepeatable: true,
args: {
name: "config_env_file",
isOptional: true,
template: "filepaths",
},
},
{
name: "--no-banner",
description: "Flag used for disabling the printed banner in tty",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
};
export default completion;
```
@@ -0,0 +1,192 @@
# `nymvisor` Binary Commands
These docs are autogenerated by the `autodocs` script.
**TODO add link**
```
Usage: nymvisor [OPTIONS] <COMMAND>
Commands:
init Initialise a nymvisor instance with persistent Config.toml file
run Run the associated daemon with the preconfigured settings
build-info Show build information of this binary
daemon-build-info Show build information of the associated daemon
add-upgrade Queues up another upgrade for the associated daemon
config Show configuration options being used by this instance of nymvisor
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file that configures the nymvisor and overrides any preconfigured values
-h, --help
Print help
-V, --version
Print version
```
### `init`
```
Initialise a nymvisor instance with persistent Config.toml file
Usage: nymvisor init [OPTIONS] <DAEMON_BINARY>
Arguments:
<DAEMON_BINARY> Path to the daemon's executable
Options:
--id <ID>
ID specifies the human readable ID of this particular nymvisor instance. Can be overridden with $NYMVISOR_ID environmental variable
--upstream-base-upgrade-url <UPSTREAM_BASE_UPGRADE_URL>
Sets the base url of the upstream source for obtaining upgrade information for the deaemon. It will be used fo constructing the full url, i.e. $NYMVISOR_UPSTREAM_BASE_UPGRADE_URL/$DAEMON_NAME/upgrade-info.json Can be overridden with $NYMVISOR_UPSTREAM_BASE_UPGRADE_URL environmental variable
--upstream-polling-rate <UPSTREAM_POLLING_RATE>
Specifies the rate of polling the upstream url for upgrade information. default: 1h Can be overridden with $NYMVISOR_UPSTREAM_POLLING_RATE
--disable-nymvisor-logs
If enabled, this will disable `nymvisor` logs (but not the underlying process) Can be overridden with $NYMVISOR_DISABLE_LOGS environmental variable
--upgrade-data-directory <UPGRADE_DATA_DIRECTORY>
Set custom directory for upgrade data - binaries and upgrade plans. If not set, the global nymvisors' data directory will be used instead. Can be overridden with $NYMVISOR_UPGRADE_DATA_DIRECTORY environmental variable
--daemon-home <DAEMON_HOME>
The location where the `nymvisor/` directory is kept that contains the auxiliary files associated with the underlying daemon, such as any backups or current version information. (e.g. $HOME/.nym/nym-api/my-nym-api, $HOME/.nym/mixnodes/my-mixnode, etc.). Can be overridden with $DAEMON_HOME environmental variable
--daemon-absolute-upstream-upgrade-url <DAEMON_ABSOLUTE_UPSTREAM_UPGRADE_URL>
Override url to the upstream source for upgrade plans for this daeamon. The Url has to point to an endpoint containing a valid [`UpgradeInfo`] json. Note: if set this takes precedence over `upstream_base_upgrade_url` Can be overridden with $DAEMON_ABSOLUTE_UPSTREAM_UPGRADE_URL environmental variable
--allow-download-upgrade-binaries <ALLOW_DOWNLOAD_UPGRADE_BINARIES>
If set to true, this will enable auto-downloading of new binaries using the url provided in the `upgrade-info.json` Can be overridden with $DAEMON_ALLOW_BINARIES_DOWNLOAD environmental variable [possible values: true, false]
--enforce-download-checksum <ENFORCE_DOWNLOAD_CHECKSUM>
If enabled nymvisor will require that a checksum is provided in the upgrade plan for the binary to be downloaded. If disabled, nymvisor will not require a checksum to be provided, but still check the checksum if one is provided. Can be overridden with $DAEMON_ENFORCE_DOWNLOAD_CHECKSUM environmental variable [possible values: true, false]
--restart-daemon-after-upgrade <RESTART_DAEMON_AFTER_UPGRADE>
If enabled, nymvisor will restart the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. Otherwise (if disabled), nymvisor will stop running after an upgrade and will require the system administrator to manually restart it. Note restart is only after the upgrade and does not auto-restart the subprocess after an error occurs. Can be overridden with $DAEMON_RESTART_AFTER_UPGRADE environmental variable [possible values: true, false]
--restart-daemon-on-failure
If enabled, nymvisor will restart the subprocess with the same command-line arguments and flags after it has crashed Can be overridden with $DAEMON_RESTART_ON_FAILURE environmental variable
--on-failure-daemon-restart-delay <ON_FAILURE_DAEMON_RESTART_DELAY>
If `restart_on_failure` is enabled, the following value defines the amount of time `nymvisor` shall wait before restarting the subprocess. Can be overridden with $DAEMON_FAILURE_RESTART_DELAY environmental variable
--max-daemon-startup-failures <MAX_DAEMON_STARTUP_FAILURES>
Defines the maximum number of startup failures the subprocess can experience in a quick succession before no further restarts will be attempted and `nymvisor` will exit. Can be overridden with $DAEMON_MAX_STARTUP_FAILURES environmental variable
--startup-period-duration <STARTUP_PERIOD_DURATION>
Defines the length of time during which the subprocess is still considered to be in the startup phase when its failures are going to be considered in `max_startup_failures`. Can be overridden with $DAEMON_STARTUP_PERIOD_DURATION environmental variable
--daemon-shutdown-grace-period <DAEMON_SHUTDOWN_GRACE_PERIOD>
Specifies the amount of time `nymvisor` is willing to wait for the subprocess to undergo graceful shutdown after receiving an interrupt (for either an upgrade or shutdown of the `nymvisor` itself) Once the time passes, a kill signal is going to be sent instead. Can be overridden with $DAEMON_SHUTDOWN_GRACE_PERIOD environmental variable
--daemon-backup-data-directory <DAEMON_BACKUP_DATA_DIRECTORY>
Set custom backup directory for daemon data. If not set, the daemon's home directory will be used instead. Can be overridden with $DAEMON_BACKUP_DATA_DIRECTORY environmental variable
--unsafe-skip-backup
If enabled, `nymvisor` will perform upgrades directly without performing any backups. default: false Can be overridden with $DAEMON_UNSAFE_SKIP_BACKUP environmental variable
-o, --output <OUTPUT>
[default: text] [possible values: text, json]
-h, --help
Print help
```
### `run`
```
Run the associated daemon with the preconfigured settings
Usage: nymvisor run [DAEMON_ARGS]...
Arguments:
[DAEMON_ARGS]...
Options:
-h, --help Print help
```
### `build-info`
```
Show build information of this binary
Usage: nymvisor build-info [OPTIONS]
Options:
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
Example output:
```
Binary Name: nymvisor
Build Timestamp: 2024-05-07T13:11:43.523733081Z
Build Version: 0.1.0
Commit SHA: 7d233a4a2fc697a2f97e504db6a3765cc9f2dd49
Commit Date: 2024-04-25T11:27:18.000000000+02:00
Commit Branch: max/auto-docs
rustc Version: 1.78.0
rustc Channel: stable
cargo Profile: release
```
### `daemon-build-info`
```
Show build information of the associated daemon
Usage: nymvisor daemon-build-info [OPTIONS]
Options:
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
Example output:
```
Binary Name: nym-mixnode
Build Timestamp: 2024-03-22T17:49:30.278145161Z
Build Version: 1.1.34
Commit SHA: 31bbfdecf5ea6deff69bd72a0bccd9dddae1003f
Commit Date: 2024-02-19T10:52:15.000000000+01:00
Commit Branch: max/docs-overhaul-1
rustc Version: 1.74.1
rustc Channel: stable
cargo Profile: release
```
### `add-upgrade`
```
Queues up another upgrade for the associated daemon
Usage: nymvisor add-upgrade [OPTIONS] --upgrade-name <UPGRADE_NAME> <DAEMON_BINARY>
Arguments:
<DAEMON_BINARY> Path to the daemon's upgrade executable
Options:
--upgrade-name <UPGRADE_NAME> Name of this upgrade
--force Overwrite existing upgrade binary / upgrade-info.json file
--add-binary Indicate that this command should only add binary to an *existing* scheduled upgrade
--now Force the upgrade to happen immediately
--publish-date <PUBLISH_DATE> Specifies the publish date metadata field of this upgrade. If unset, the current time will be used
--upgrade-time <UPGRADE_TIME> Specifies the time at which the provided upgrade will be performed (RFC3339 formatted). If left unset, the upgrade will be performed in 15min
--upgrade-delay <UPGRADE_DELAY> Specifies delay until the provided upgrade is going to get performed. If let unset, the upgrade will be performed in 15min
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
### `config`
```
Show configuration options being used by this instance of nymvisor
Usage: nymvisor config [OPTIONS]
Options:
-o, --output <OUTPUT> [default: text] [possible values: text, json]
-h, --help Print help
```
Example output:
```
id: nym-mixnode-default
daemon name: nym-mixnode
daemon home: /home/xen/.nym/mixnodes/my-node
upstream base upgrade url: https://nymtech.net/.wellknown/
disable nymvisor logs: false
CUSTOM upgrade data directory ""
upstream absolute upgrade url: ""
allow binaries download: true
enforce download checksum: true
restart after upgrade: true
restart on failure: false
on failure restart delay: 10s
max startup failures: 10
startup period duration: 2m
shutdown grace period: 10s
CUSTOM backup data directory ""
UNSAFE skip backups false
```
+311
View File
@@ -0,0 +1,311 @@
use log::info;
use std::fs::File;
use std::io::{self, Write};
use std::process::{Command, Output};
use std::{fs, vec};
const PATH: &str = "./autodoc-generated-markdown/";
fn main() -> io::Result<()> {
env_logger::init();
// TODO try and write to grab the commands from Clap at some point
let commands_with_subcommands = vec![
(
"../../target/release/nym-api",
vec!["init", "run", "build-info"],
),
(
"../../target/release/nym-client",
vec![
"init",
"run",
"import-credential",
"list-gateways",
"switch-gateway",
"build-info",
"completions",
"generate-fig-spec",
],
),
(
"../../target/release/nym-socks5-client",
vec![
"init",
"run",
"import-credential",
"list-gateways",
"add-gateway",
"build-info",
"completions",
"generate-fig-spec",
],
),
(
"../../target/release/nym-node",
vec![
"build-info",
"bonding-information",
"node-details",
"migrate",
"run",
"sign",
],
),
(
"../../target/release/nymvisor",
vec![
"init",
"run",
"build-info",
"daemon-build-info",
"add-upgrade",
"config",
],
),
];
let commands_with_subsubcommands = vec![(
"../../target/release/nym-cli",
vec![
(
"account",
vec!["create", "balance", "pub-key", "send", "send-multiple"],
),
("signature", vec!["sign", "verify"]),
(
"coconut",
vec![
"generate-freepass",
"issue-credentials",
"recover-credentials",
"import-credential",
],
),
("block", vec!["get", "time", "current-height"]),
(
"cosmwasm",
vec![
"upload",
"init",
"generate-init-message",
"migrate",
"execute",
],
),
("tx", vec!["get", "query"]),
(
"vesting-schedule",
vec!["create", "query", "vested-balance", "withdraw-vested"],
),
("mixnet", vec!["query", "delegators", "operators"]),
("generate-fig", vec![""]),
],
)];
for (main_command, subcommands) in commands_with_subcommands {
let last_word = get_last_word_from_filepath(main_command);
info!("{last_word:#?}");
if !fs::metadata(PATH)
.map(|metadata| metadata.is_dir())
.unwrap_or(false)
{
fs::create_dir_all(PATH)?;
}
let mut file = File::create(format!("{}/{}-commands.md", PATH, last_word.unwrap()))?;
writeln!(
file,
"# {} Binary Commands",
format!("`{}`", last_word.unwrap())
)?;
writeln!(
file,
"\nThese docs are autogenerated by the `autodocs` script.
\n**TODO add link**"
)?;
let output = Command::new(main_command).arg("--help").output()?;
write_output_to_file(&mut file, output)?;
for subcommand in subcommands {
execute_command(&mut file, main_command, subcommand, None)?;
}
}
// nym-cli has subsubcommands so needs its own loop
for (main_command, subcommands) in &commands_with_subsubcommands {
let last_word = get_last_word_from_filepath(main_command);
info!("{last_word:#?}");
let mut file = File::create(format!("{}/{}-commands.md", PATH, last_word.unwrap()))?;
writeln!(
file,
"# {} Binary Commands",
format!("`{}`", last_word.unwrap())
)?;
writeln!(
file,
"\nThese docs are autogenerated by the `autodocs` script.
\n**TODO add link**"
)?;
let output = Command::new(main_command).arg("--help").output()?;
write_output_to_file(&mut file, output)?;
for (subcommand, subsubcommands) in subcommands {
writeln!(file, "\n### `{}` ", subcommand)?;
let output = Command::new(main_command)
.arg(subcommand)
.arg("--help")
.output()?;
if !output.stdout.is_empty() {
write_output_to_file(&mut file, output)?;
} else {
info!("empty stdout - nothing to write");
}
for subsubcommand in subsubcommands {
execute_command(&mut file, main_command, subcommand, Some(subsubcommand))?;
/*
println!(
"{:#?} {:#?} {:#?}",
last_word.unwrap(),
subcommand,
subsubcommand
);
*/
}
}
}
Ok(())
}
fn get_last_word_from_filepath(filepath: &str) -> Option<&str> {
let parts: Vec<&str> = filepath.split('/').collect();
parts.last().copied()
}
fn execute_command(
file: &mut File,
main_command: &str,
subcommand: &str,
subsubcommand: Option<&str>,
) -> io::Result<()> {
// checking for the nym-cli subsubcommands
if subsubcommand.is_some() {
writeln!(file, "\n### `{} {}`", subcommand, subsubcommand.unwrap())?;
info!("executing {} {} --help ", main_command, subcommand);
let output = Command::new(main_command)
.arg(subcommand)
.arg(subsubcommand.unwrap())
.arg("--help")
.output()?;
if !output.stdout.is_empty() {
write_output_to_file(file, output)?;
} else {
info!("empty stdout - nothing to write");
}
// just subcommands
} else {
writeln!(file, "\n### `{}`", subcommand)?;
// println!("{}", subcommand);
// execute help
let output = Command::new(main_command)
.arg(subcommand)
.arg("--help")
.output()?;
if !output.stdout.is_empty() {
write_output_to_file(file, output)?;
} else {
info!("empty stdout - nothing to write");
}
// then execute w/out help: the majority of functions will fail since you're not passing
// required params but thats fine as we can just not render stderr into the final file.
//
// this check is basically checking for the rare commands (rn just one) that start a process with no params
// perhaps if this list grows we could just add a timeout and shunt the running and writing
// into a thread with a timeout or something but for right now its fine / thats overkill
if get_last_word_from_filepath(main_command).unwrap() == "nym-node"
|| get_last_word_from_filepath(main_command).unwrap() == "nym-api"
|| get_last_word_from_filepath(main_command).unwrap() == "nymvisor"
&& subcommand == "run"
{
info!("SKIPPING {} {}", main_command, subcommand);
} else {
info!("executing {} {}", main_command, subcommand);
let output = Command::new(main_command).arg(subcommand).output()?;
if !output.stdout.is_empty() {
writeln!(file, "Example output:")?;
write_output_to_file(file, output)?;
} else {
info!("empty stdout - nothing to write");
if !&output.stderr.is_empty() {
info!("stderr: {:#?}", String::from_utf8_lossy(&output.stderr));
}
}
}
}
Ok(())
}
/*
fn execute_command(file: &mut File, main_command: &str, subcommand: &str) -> io::Result<()> {
// title
writeln!(file, "\n### `{}`", subcommand)?;
// first execute the command with `--help`
info!("executing {} {} --help ", main_command, subcommand);
let output = Command::new(main_command)
.arg(subcommand)
.arg("--help")
.output()?;
if !output.stdout.is_empty() {
write_output_to_file(file, output)?;
} else {
info!("empty stdout - nothing to write");
}
// then execute w/out help: the majority of functions will fail since you're not passing
// required params but thats fine as we can just not render stderr into the final file.
//
// this check is basically checking for the rare commands (rn just one) that start a process with no params
// perhaps if this list grows we could just add a timeout and shunt the running and writing
// into a thread with a timeout or something but for right now its fine / thats overkill
if get_last_word_from_filepath(main_command).unwrap() == "nym-node"
|| get_last_word_from_filepath(main_command).unwrap() == "nym-api"
|| get_last_word_from_filepath(main_command).unwrap() == "nymvisor" && subcommand == "run"
{
info!("SKIPPING {} {}", main_command, subcommand);
} else {
info!("executing {} {}", main_command, subcommand);
let output = Command::new(main_command).arg(subcommand).output()?;
if !output.stdout.is_empty() {
writeln!(file, "Example output:")?;
write_output_to_file(file, output)?;
} else {
info!("empty stdout - nothing to write");
if !&output.stderr.is_empty() {
info!("stderr: {:#?}", String::from_utf8_lossy(&output.stderr));
}
}
}
Ok(())
}
*/
fn write_output_to_file(file: &mut File, output: Output) -> io::Result<()> {
writeln!(file, "```")?;
file.write_all(&output.stdout)?;
writeln!(file, "```")?;
// if we want to keep this we could create 2 copies if you run this in a certain mode ("debug"
// oder so) so you can see the stderr but in reality, we want dont want the errors in teh md
// file and also probaly not write 'stdout', but just have <command><output> in a clean fashion
if !&output.stderr.is_empty() {
info!("stderr: {:#?}", String::from_utf8_lossy(&output.stderr));
}
Ok(())
}
-2
View File
@@ -24,8 +24,6 @@
- [Linux](nymvpn/gui-linux.md)
- [MacOS](nymvpn/gui-mac.md)
- [CLI](nymvpn/cli.md)
- [Linux](nymvpn/cli-linux.md)
- [MacOS](nymvpn/cli-mac.md)
- [Troubleshooting](nymvpn/troubleshooting.md)
- [NymVPN FAQ](nymvpn/faq.md)
- [NymConnect X Monero](tutorials/monero.md)
@@ -1,4 +1,4 @@
# NymVPN alpha CLI: Guide for GNU/Linux
# NymVPN alpha CLI Guide
```admonish info
NymVPN is an experimental software and it's for testing purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
@@ -8,7 +8,7 @@ NymVPN is an experimental software and it's for testing purposes only. All users
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Open Github [releases page]({{nym_vpn_releases}}) and download the binary for Debian based Linux
1. Open Github [releases page]({{nym_vpn_releases}}) and download the CLI latest binary for your system
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
@@ -25,17 +25,12 @@ tar -xvf <BINARY>.tar.gz
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
```
4. Make executable by running:
4. Make executable:
```sh
# make sure you are in the right sub-directory
chmod u+x ./nym-vpn-cli
```
5. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
## Run NymVPN
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
@@ -47,7 +42,7 @@ Make sure your terminal is open in the same directory as your `nym-vpn-cli` bina
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WIREGUARD_IP>
```
3. To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways) and pick one
3. To choose different Gateways, visit [explorer.nymtech.net/network-components/gateways](https://explorer.nymtech.net/network-components/gateways) and copy-paste an identity key of your choice
4. See all possibilities in [command explanation](#cli-commands-and-options) section below
In case of errors, see [troubleshooting section](troubleshooting.md).
@@ -56,16 +51,16 @@ In case of errors, see [troubleshooting section](troubleshooting.md).
The basic syntax of `nym-vpn-cli` is:
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
sudo ./nym-vpn-cli <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
```
* To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
* To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://explorer.nymtech.net/network-components/gateways)
* To see all possibilities run with `--help` flag:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```sh
Usage: nym-vpn-cli [OPTIONS]
Usage: nym-vpn-cli [OPTIONS] <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
@@ -76,11 +71,13 @@ Options:
Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO
--entry-gateway-low-latency
Auto-select entry gateway by latency
--exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID>
--exit-router-country <EXIT_ROUTER_COUNTRY>
--exit-gateway-country <EXIT_GATEWAY_COUNTRY>
Mixnet recipient address
--enable-wireguard
Enable the wireguard traffic between the client and the entry gateway
@@ -88,8 +85,10 @@ Options:
Associated private key
--wg-ip <WG_IP>
The IP address of the wireguard interface used for the first hop to the entry gateway
--nym-ip <NYM_IP>
The IP address of the nym TUN device that wraps IP packets in sphinx packets
--nym-ipv4 <NYM_IPV4>
The IPv4 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-ipv6 <NYM_IPV6>
The IPv6 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-mtu <NYM_MTU>
The MTU of the nym TUN device that wraps IP packets in sphinx packets
--disable-routing
@@ -125,3 +124,19 @@ Here is a list of the options and their descriptions. Some are essential, some a
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
## Testnet environment
If you want to run NymVPN CLI in Nym Sandbox environment, there are a few adjustments to be done:
1. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
1. Check available Gateways at [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
2. Run with a flag `-c`
```sh
sudo ./nym-vpn-cli -c <PATH_TO>/sandbox.env <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
```
@@ -0,0 +1,35 @@
# NymVPN Command Line Interface (CLI)
```admonish info
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
```
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
## Automated Script for CLI Installation
We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8fb31) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
```sh
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/d39f98dbb36ccff761a7e940073388a6fe7b73fe/execute-nym-vpn-cli-binary.sh && chmod u+x execute-nym-vpn-cli-binary.sh && sudo -E ./execute-nym-vpn-cli-binary.sh
```
2. Follow the prompts in the program
3. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
* It prints a JSON view of existing Gateways and prompt you to:
- *Make sure to use two different Gateways for entry and exit!*
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
To run `nym-vpn-cli` again, reconnect your wifi, move to the directory of your CLI binary `cd ~/nym-vpn-cli-dir` and follow the guide for [Linux](cli-linux.md#run-nymvpn) or [MacOS](cli-mac.md#run-nymvpn). If you find it too difficult, just run this script again - like in step \#3 above.
In case of errors check out the [troubleshooting](troubleshooting.md) section.
+137 -21
View File
@@ -1,35 +1,151 @@
# NymVPN Command Line Interface (CLI)
# NymVPN alpha CLI Guide
```admonish info
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
NymVPN is an experimental software and it's for testing purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
```
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
## Installation
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
## Automated Script for CLI Installation
1. Open Github [releases page]({{nym_vpn_releases}}) and download the CLI latest binary for your system
We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8fb31) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/d39f98dbb36ccff761a7e940073388a6fe7b73fe/execute-nym-vpn-cli-binary.sh && chmod u+x execute-nym-vpn-cli-binary.sh && sudo -E ./execute-nym-vpn-cli-binary.sh
echo "<SHA_STRING>" | shasum -a 256 -c
# choose a correct one according to your binary, this is just an example
# echo "0e4abb461e86b2c168577e0294112a3bacd3a24bf8565b49783bfebd9b530e23 nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_amd64.tar.gz" | shasum -a 256 -c
```
2. Follow the prompts in the program
3. Extract files:
```sh
tar -xvf <BINARY>.tar.gz
# for example
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
```
3. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
4. Make executable:
```sh
# make sure you are in the right sub-directory
chmod u+x nym-vpn-cli
```
* It prints a JSON view of existing Gateways and prompt you to:
- *Make sure to use two different Gateways for entry and exit!*
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
## Run NymVPN
To run `nym-vpn-cli` again, reconnect your wifi, move to the directory of your CLI binary `cd ~/nym-vpn-cli-dir` and follow the guide for [Linux](cli-linux.md#run-nymvpn) or [MacOS](cli-mac.md#run-nymvpn). If you find it too difficult, just run this script again - like in step \#3 above.
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
In case of errors check out the [troubleshooting](troubleshooting.md) section.
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
1. Run it as root with `sudo` - the command will look like this with specified arguments:
```sh
# choose only one conditional --argument listed in {brackets}
sudo ./nym-vpn-cli { --exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY> }
```
2. To choose different Gateways, visit [explorer.nymtech.net/network-components/gateways](https://explorer.nymtech.net/network-components/gateways) and copy-paste an identity key of your choice
```admonish note
Nym Exit Gateway functionality was implemented just recently and not all the Gateways are upgraded and ready to handle the VPN connections. If you want to make sure you are connecting to a Gateway with an embedded Network Requester, IP Packet Router and applied Nym exit policy, visit [this page](https://nymtech.net/events/fast-and-furious), scroll down to the list and search Gateways with all the functionalities enabled.
```
3. See all possibilities in [command explanation](#cli-commands-and-options) section below
4. In case of errors, see [troubleshooting section](troubleshooting.md)
### CLI Commands and Options
The basic syntax of `nym-vpn-cli` is:
```sh
# choose only one conditional --argument listed in {brackets}
sudo ./nym-vpn-cli { --exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY> }
```
To see all the possibilities run with `--help` flag:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```sh
Usage: nym-vpn-cli [OPTIONS] <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file describing the network
--mixnet-client-path <MIXNET_CLIENT_PATH>
Path to the data directory of a previously initialised mixnet client, where the keys reside
--entry-gateway-id <ENTRY_GATEWAY_ID>
Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO
--entry-gateway-low-latency
Auto-select entry gateway by latency
--exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID>
--exit-gateway-country <EXIT_GATEWAY_COUNTRY>
Mixnet recipient address
--enable-wireguard
Enable the wireguard traffic between the client and the entry gateway
--private-key <PRIVATE_KEY>
Associated private key
--wg-ip <WG_IP>
The IP address of the wireguard interface used for the first hop to the entry gateway
--nym-ipv4 <NYM_IPV4>
The IPv4 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-ipv6 <NYM_IPV6>
The IPv6 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-mtu <NYM_MTU>
The MTU of the nym TUN device that wraps IP packets in sphinx packets
--disable-routing
Disable routing all traffic through the nym TUN device. When the flag is set, the nym TUN device will be created, but to route traffic through it you will need to do it manually, e.g. ping -Itun0
--enable-two-hop
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
--enable-poisson-rate
Enable Poisson process rate limiting of outbound traffic
--disable-background-cover-traffic
Disable constant rate background loop cover traffic
-h, --help
Print help
-V, --version
Print version
```
~~~
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to be adjusted by users.
**Fundamental commands and arguments**
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`)
- `--exit-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`)
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`)
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
**Advanced options**
- `-c` is a path to an enviroment config, like [`sandbox.env`](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env)
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
## Testnet environment
If you want to run NymVPN CLI in Nym Sandbox environment, there are a few adjustments to be done:
1. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
1. Check available Gateways at [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
2. Run with a flag `-c`
```sh
sudo ./nym-vpn-cli -c <PATH_TO>/sandbox.env <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
```
+4 -4
View File
@@ -1,12 +1,12 @@
# NymVPN - Desktop (GUI)
```admonish info
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
Our alpha testing round is done with participants at live workshop events and the application in this stage may not work for everyone.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
```
This is the alpha version of NymVPN desktop application (GUI). A demo of how the client will look like for majority of day-to-day users.
This is a desktop (GUI) version of NymVPN client. A demo of how the application will look like for majority of day-to-day users.
Follow the simple [automated script](#automated-script-for-gui-installation) below to install and run NymVPN GUI. If the script didn't work for your distribution or you prefer to do a manual setup follow the steps in the guide for [Linux](gui-linux.md) or [MacOS](gui-mac.md) .
@@ -14,12 +14,12 @@ Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums
## Linux AppImage Automated Installation Method
The latest releases contain `appimage.sh` script. This method makes the installation simple for Linux users who want to run NymVPN from AppImmage. Executing the command below will download the binary to `~/.local/bin` and verify the checksum.
The latest releases contain `appimage.sh` script. This method makes the installation simple for Linux users who want to run NymVPN from AppImmage. Executing the command below will download the binary to `~/.local/bin` and verify the checksum:
```sh
curl -fsSL https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-desktop-v<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->/appimage.sh | bash
```
Run with the command
Run with the command:
```sh
sudo -E ~/.local/bin/nym-vpn.appimage
```
+3 -4
View File
@@ -5,16 +5,15 @@
**Nym proudly presents NymVPN alpha** - a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features).
**You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md) on the Sandbox testnet environment.
**You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md).
**Here is how**
1. Go to the NymVPN [testers form]({{nym_vpn_form_url}})
2. Please consent to the GDPR so we can use the results
2. Fill and submit the [form!]({{nym_vpn_form_url}})
3. To test the GUI, [go here](gui.md)
4. To test the CLI, [go here](cli.md)
5. Fill and submit the [form!]({{nym_vpn_form_url}})
6. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
5. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
***NymVPN alpha testing will last from 15th of January - 15th of February.***
@@ -2,6 +2,14 @@
Below are listed some points which may need to be addressed when testing NymVPN alpha. If you crashed into any errors which are not listed, please contact us at the testing workshop or in the NymVPN [Matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat).
#### NymVPN attempts to connect to sandbox testnet
If you testing the latest versions and you correctly expect the client to run over the mainnet, but it listens to `https://sandbox-nym-api1.nymtech.net`, it's probably because of your previous configuration.
Check your `config.toml` either in the directory from which you run your client or in `~/.config/nym-vpn/` and remove `sandbox.env` from the config file and folder.
If the problem persists (probably due to some locally cache) download [`mainnet.env`](https://github.com/nymtech/nym/blob/master/envs/mainnet.env) and save it to the same directory.
#### Running GUI failed due to `TOML parse error`
If you see this error when running NymVPN alpha desktop, it's because the older versions needed entry location in `config.toml` configuration file. From `v0.0.3` the entry location is selected directly by the user in the application. This error is due to an old `app-data.toml` config in your computer.
+4 -4
View File
@@ -13,10 +13,10 @@ DENOMS_EXPONENT=6
REWARDING_VALIDATOR_ADDRESS=n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa
MIXNET_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
VESTING_CONTRACT_ADDRESS=n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n13902g92xfefeyzuyed49snlm5fxv5ms6mdq5kvrut27hasdw5a9q9vyw6c
GROUP_CONTRACT_ADDRESS=n18nczmqw6adwxg2wnlef3hf0etf8anccafp2pjpul5rrtmv96umyq5mv7t5
MULTISIG_CONTRACT_ADDRESS=n1q3zzxl78rlmxv3vn0uf4vkyz285lk8q2xzne299yt9x6mpfgk90qukuzmv
COCONUT_DKG_CONTRACT_ADDRESS=n1jsz20ggp5a6v76j060erkzvxmeus8htlpl77yxp878f0gf95cyaq6p2pee
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ljlwey4xdj0zs7zueepc48nkr033fca6fjgvurfvttqegm8dvsrswsul70
GROUP_CONTRACT_ADDRESS=n10v3rjnq4cjyccfykyams68ztce337gksuu6f0lvtl4meuwvkewaqru4uav
MULTISIG_CONTRACT_ADDRESS=n1cemnu8as0ls45v3caunpesl8jlsfw2ff9rlwnltlecp7zrxct4dsqc2y42
COCONUT_DKG_CONTRACT_ADDRESS=n1zx96qgd88vqlzcxkpwzks7kqs5ctrx36xtzfc58p7q6c4ng9anlqzc4nh8
NAME_SERVICE_CONTRACT_ADDRESS=n12ne7qtmdwd0j03t9t5es8md66wq4e5xg9neladrsag8fx3y89rcs36asfp
SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1ps5yutd7sufwg058qd7ac7ldnlazsvmhzqwucsfxmm445d70u8asqxpur4
EPHEMERA_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.33"
version = "1.1.34"
edition = "2021"
license.workspace = true
+3 -2
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-gateway"
license = "GPL-3.0"
version = "1.1.33"
version = "1.1.35"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
@@ -38,6 +38,7 @@ sqlx = { workspace = true, features = [
"sqlite",
"macros",
"migrate",
"time"
] }
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
thiserror = { workspace = true }
@@ -49,7 +50,7 @@ tokio = { workspace = true, features = [
"time",
] }
tokio-stream = { version = "0.1.11", features = ["fs"] }
tokio-tungstenite = { vworkspace = true }
tokio-tungstenite = { workspace = true }
tokio-util = { workspace = true, features = ["codec"] }
url = { workspace = true, features = ["serde"] }
time = { workspace = true }
@@ -4,7 +4,7 @@
use nym_crypto::asymmetric::identity;
use thiserror::Error;
#[derive(Debug, Error)]
#[derive(Debug, Clone, Error)]
pub enum HandshakeError {
#[error(
"received key material of invalid length - {0}. Expected: {}",
+25
View File
@@ -190,6 +190,22 @@ impl ClientControlRequest {
}
}
pub fn name(&self) -> String {
match self {
ClientControlRequest::Authenticate { .. } => "Authenticate".to_string(),
ClientControlRequest::RegisterHandshakeInitRequest { .. } => {
"RegisterHandshakeInitRequest".to_string()
}
ClientControlRequest::BandwidthCredential { .. } => "BandwidthCredential".to_string(),
ClientControlRequest::BandwidthCredentialV2 { .. } => {
"BandwidthCredentialV2".to_string()
}
ClientControlRequest::ClaimFreeTestnetBandwidth => {
"ClaimFreeTestnetBandwidth".to_string()
}
}
}
pub fn new_enc_coconut_bandwidth_credential_v1(
credential: &OldV1Credential,
shared_key: &SharedKeys,
@@ -291,6 +307,15 @@ pub enum ServerResponse {
}
impl ServerResponse {
pub fn name(&self) -> String {
match self {
ServerResponse::Authenticate { .. } => "Authenticate".to_string(),
ServerResponse::Register { .. } => "Register".to_string(),
ServerResponse::Bandwidth { .. } => "Bandwidth".to_string(),
ServerResponse::Send { .. } => "Send".to_string(),
ServerResponse::Error { .. } => "Error".to_string(),
}
}
pub fn new_error<S: Into<String>>(msg: S) -> Self {
ServerResponse::Error {
message: msg.into(),
@@ -0,0 +1,7 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
ALTER TABLE available_bandwidth
ADD COLUMN freepass_expiration TIMESTAMP WITHOUT TIME ZONE;
+13
View File
@@ -42,6 +42,9 @@ const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5);
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
@@ -516,6 +519,13 @@ pub struct Debug {
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Defines maximum delay between client bandwidth information being flushed to the persistent storage.
#[serde(with = "humantime_serde")]
pub client_bandwidth_max_flushing_rate: Duration,
/// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage.
pub client_bandwidth_max_delta_flushing_amount: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
@@ -533,6 +543,9 @@ impl Default for Debug {
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
client_bandwidth_max_flushing_rate: DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE,
client_bandwidth_max_delta_flushing_amount:
DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT,
use_legacy_framed_packet_version: false,
}
}
+1
View File
@@ -165,6 +165,7 @@ impl From<ConfigV1_1_31> for Config {
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
..Default::default()
},
}
}
+1
View File
@@ -33,6 +33,7 @@ fn load_gateway_details(
};
Ok(api_requests::v1::gateway::models::Gateway {
enforces_zk_nyms: config.gateway.only_coconut_credentials,
client_interfaces: api_requests::v1::gateway::models::ClientInterfaces {
wireguard,
mixnet_websockets: Some(api_requests::v1::gateway::models::WebSockets {
+23 -17
View File
@@ -42,18 +42,35 @@ pub struct Bandwidth {
}
impl Bandwidth {
pub const fn new(value: u64) -> Bandwidth {
pub const fn new_unchecked(value: u64) -> Bandwidth {
Bandwidth { value }
}
pub fn try_from_raw_value(value: &str, typ: CredentialType) -> Result<Self, BandwidthError> {
let bandwidth_value =
pub fn new(bandwidth_value: u64) -> Result<Bandwidth, BandwidthError> {
if bandwidth_value > i64::MAX as u64 {
// note that this would have represented more than 1 exabyte,
// which is like 125,000 worth of hard drives, so I don't think we have
// to worry about it for now...
warn!("Somehow we received bandwidth value higher than 9223372036854775807. We don't really want to deal with this now");
return Err(BandwidthError::UnsupportedBandwidthValue(bandwidth_value));
}
Ok(Bandwidth {
value: bandwidth_value,
})
}
pub(crate) fn parse_raw_bandwidth(
value: &str,
typ: CredentialType,
) -> Result<(u64, Option<OffsetDateTime>), BandwidthError> {
let (bandwidth_value, freepass_expiration) =
match typ {
CredentialType::Voucher => {
let token_value: u64 = value
.parse()
.map_err(|source| BandwidthError::VoucherValueParsingFailure { source })?;
token_value * nym_network_defaults::BYTES_PER_UTOKEN
(token_value * nym_network_defaults::BYTES_PER_UTOKEN, None)
}
CredentialType::FreePass => {
let expiry_timestamp: i64 = value
@@ -70,21 +87,10 @@ impl Bandwidth {
if expiry_date < now {
return Err(BandwidthError::ExpiredFreePass { expiry_date });
}
nym_network_defaults::BYTES_PER_FREEPASS
(nym_network_defaults::BYTES_PER_FREEPASS, Some(expiry_date))
}
};
if bandwidth_value > i64::MAX as u64 {
// note that this would have represented more than 1 exabyte,
// which is like 125,000 worth of hard drives, so I don't think we have
// to worry about it for now...
warn!("Somehow we received bandwidth value higher than 9223372036854775807. We don't really want to deal with this now");
return Err(BandwidthError::UnsupportedBandwidthValue(bandwidth_value));
}
Ok(Bandwidth {
value: bandwidth_value,
})
Ok((bandwidth_value, freepass_expiration))
}
pub fn value(&self) -> u64 {
+2 -1
View File
@@ -8,4 +8,5 @@ mod bandwidth;
pub(crate) mod embedded_clients;
pub(crate) mod websocket;
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new(64 * 1024 * 1024 * 1024); // 64GB
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth =
Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
use crate::node::client_handling::websocket::connection_handler::BandwidthFlushingBehaviourConfig;
use nym_crypto::asymmetric::identity;
use std::sync::Arc;
// I can see this being possible expanded with say storage or client store
#[derive(Clone)]
pub(crate) struct CommonHandlerState {
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
pub(crate) local_identity: Arc<identity::KeyPair>,
pub(crate) only_coconut_credentials: bool,
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
}
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::bandwidth::BandwidthError;
use crate::node::client_handling::websocket::connection_handler::ClientBandwidth;
use crate::node::{
client_handling::{
bandwidth::Bandwidth,
@@ -34,6 +35,7 @@ use nym_validator_client::coconut::CoconutApiError;
use rand::{CryptoRng, Rng};
use std::{process, time::Duration};
use thiserror::Error;
use time::OffsetDateTime;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
@@ -42,6 +44,11 @@ pub enum RequestHandlingError {
#[error("Internal gateway storage error")]
StorageError(#[from] StorageError),
#[error(
"the database entry for bandwidth of the registered client {client_address} is missing!"
)]
MissingClientBandwidthEntry { client_address: String },
#[error("Provided bandwidth IV is malformed - {0}")]
MalformedIV(#[from] IVConversionError),
@@ -51,8 +58,8 @@ pub enum RequestHandlingError {
#[error("Provided binary request was malformed - {0}")]
InvalidTextRequest(<ClientControlRequest as TryFrom<String>>::Error),
#[error("The received request is not valid in the current context")]
IllegalRequest,
#[error("The received request is not valid in the current context: {additional_context}")]
IllegalRequest { additional_context: String },
#[error("Provided bandwidth credential did not verify correctly on {0}")]
InvalidBandwidthCredential(String),
@@ -90,9 +97,18 @@ pub enum RequestHandlingError {
#[error("the provided credential did not contain a valid type attribute")]
InvalidTypeAttribute,
#[error("insufficient bandwidth available to process the request. required: {required}B, available: {available}B")]
OutOfBandwidth { required: i64, available: i64 },
#[error("the provided credential did not have a bandwidth attribute")]
MissingBandwidthAttribute,
#[error("attempted to claim a bandwidth voucher for an account using a free pass (it expires on {expiration})")]
BandwidthVoucherForFreePassAccount { expiration: OffsetDateTime },
#[error("attempted to claim another free pass for the account while another free pass is still active (it expires on {expiration})")]
PreexistingFreePass { expiration: OffsetDateTime },
#[error("the DKG contract is unavailable")]
UnavailableDkgContract,
}
@@ -121,6 +137,7 @@ impl IntoWSMessage for Result<ServerResponse, RequestHandlingError> {
pub(crate) struct AuthenticatedHandler<R, S, St> {
inner: FreshHandler<R, S, St>,
client: ClientDetails,
client_bandwidth: ClientBandwidth,
mix_receiver: MixMessageReceiver,
// Occasionally the handler is requested to ping the connected client for confirm that it's
// active, such as when a duplicate connection is detected. This hashmap stores the oneshot
@@ -153,19 +170,32 @@ where
/// * `fresh`: fresh, unauthenticated, connection handler.
/// * `client`: details (i.e. address and shared keys) of the registered client
/// * `mix_receiver`: channel used for receiving messages from the mixnet destined for this client.
pub(crate) fn upgrade(
pub(crate) async fn upgrade(
fresh: FreshHandler<R, S, St>,
client: ClientDetails,
mix_receiver: MixMessageReceiver,
is_active_request_receiver: IsActiveRequestReceiver,
) -> Self {
AuthenticatedHandler {
) -> Result<Self, RequestHandlingError> {
// note: the `upgrade` function can only be called after registering or authenticating the client,
// meaning the appropriate database rows must have been created
// so in theory we could just unwrap the value here, but since we're returning a Result anyway,
// we might as well return a failure response instead
let bandwidth = fresh
.storage
.get_available_bandwidth(client.address)
.await?
.ok_or(RequestHandlingError::MissingClientBandwidthEntry {
client_address: client.address.as_base58_string(),
})?;
Ok(AuthenticatedHandler {
inner: fresh,
client,
client_bandwidth: ClientBandwidth::new(bandwidth.into()),
mix_receiver,
is_active_request_receiver,
is_active_ping_pending_reply: None,
}
})
}
/// Explicitly removes handle from the global store.
@@ -175,15 +205,10 @@ where
.disconnect(self.client.address)
}
/// Checks the amount of bandwidth available for the connected client.
async fn get_available_bandwidth(&self) -> Result<i64, RequestHandlingError> {
let bandwidth = self
.inner
.storage
.get_available_bandwidth(self.client.address)
.await?
.unwrap_or_default();
Ok(bandwidth)
async fn expire_freepass(&mut self) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth = Default::default();
self.client_bandwidth.update_flush_data();
Ok(self.inner.expire_freepass(self.client.address).await?)
}
/// Increases the amount of available bandwidth of the connected client by the specified value.
@@ -191,11 +216,27 @@ where
/// # Arguments
///
/// * `amount`: amount to increase the available bandwidth by.
async fn increase_bandwidth(&self, bandwidth: Bandwidth) -> Result<(), RequestHandlingError> {
async fn increase_bandwidth(
&mut self,
bandwidth: Bandwidth,
) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth.bytes += bandwidth.value() as i64;
// any increases to bandwidth should get flushed immediately
// (we don't want to accidentally miss somebody claiming a gigabyte voucher)
self.flush_bandwidth().await
}
async fn set_freepass_expiration(
&mut self,
expiration: OffsetDateTime,
) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth.freepass_expiration = Some(expiration);
self.inner
.storage
.increase_bandwidth(self.client.address, bandwidth.value() as i64)
.set_freepass_expiration(self.client.address, expiration)
.await?;
self.client_bandwidth.update_flush_data();
Ok(())
}
@@ -204,11 +245,18 @@ where
/// # Arguments
///
/// * `amount`: amount to decrease the available bandwidth by.
async fn consume_bandwidth(&self, amount: i64) -> Result<(), RequestHandlingError> {
self.inner
.storage
.consume_bandwidth(self.client.address, amount)
.await?;
async fn consume_bandwidth(&mut self, amount: i64) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth.bytes -= amount;
// since we're going to be operating on a fair use policy anyway, even if we crash and let extra few packets
// through, that's completely fine
if self
.client_bandwidth
.should_flush(self.inner.shared_state.bandwidth_cfg)
{
self.flush_bandwidth().await?;
}
Ok(())
}
@@ -232,6 +280,22 @@ where
let serial_number = credential.data.blinded_serial_number();
trace!("processing credential {}", serial_number.to_bs58());
// if we already have had received a free pass (that's not expired, don't accept any additional bandwidth)
if self.client_bandwidth.bandwidth.freepass_expired() {
// the free pass we used before has expired -> reset our state and handle the request as normal
self.expire_freepass().await?;
} else if let Some(expiration) = self.client_bandwidth.bandwidth.freepass_expiration {
// the free pass is still valid -> return error
return match credential.data.typ {
CredentialType::Voucher => {
Err(RequestHandlingError::BandwidthVoucherForFreePassAccount { expiration })
}
CredentialType::FreePass => {
Err(RequestHandlingError::PreexistingFreePass { expiration })
}
};
}
let already_spent = self
.inner
.storage
@@ -247,12 +311,6 @@ where
credential.data.epoch_id
);
let aggregated_verification_key = self
.inner
.coconut_verifier
.verification_key(credential.data.epoch_id)
.await?;
if !credential.data.validate_type_attribute() {
trace!("mismatch in the type attribute");
return Err(RequestHandlingError::InvalidTypeAttribute);
@@ -264,41 +322,52 @@ where
};
// this will extract token amounts out of bandwidth vouchers and validate expiry of free passes
let bandwidth = Bandwidth::try_from_raw_value(bandwidth_attribute, credential.data.typ)?;
let (raw_bandwidth, freepass_expiration) =
Bandwidth::parse_raw_bandwidth(bandwidth_attribute, credential.data.typ)?;
let bandwidth = Bandwidth::new(raw_bandwidth)?;
trace!("embedded bandwidth: {bandwidth:?}");
// locally verify the credential
let params = bandwidth_credential_params();
if !credential.data.verify(params, &aggregated_verification_key) {
trace!("the credential did not verify correctly");
return Err(RequestHandlingError::InvalidBandwidthCredential(
String::from("local credential verification has failed"),
));
{
let aggregated_verification_key = self
.inner
.shared_state
.coconut_verifier
.verification_key(credential.data.epoch_id)
.await?;
let params = bandwidth_credential_params();
if !credential.data.verify(params, &aggregated_verification_key) {
trace!("the credential did not verify correctly");
return Err(RequestHandlingError::InvalidBandwidthCredential(
String::from("local credential verification has failed"),
));
}
}
let was_freepass = match credential.data.typ {
match credential.data.typ {
CredentialType::Voucher => {
trace!("the credential is a bandwidth voucher. attempting to release the funds");
let api_clients = self
.inner
.shared_state
.coconut_verifier
.api_clients(credential.data.epoch_id)
.await?;
self.inner
.shared_state
.coconut_verifier
.release_bandwidth_voucher_funds(&api_clients, credential)
.await?;
false
}
CredentialType::FreePass => {
// no need to do anything special here, we already extracted the bandwidth amount and checked expiry
info!("received a free pass credential");
true
}
};
}
// technically this is not atomic, i.e. checking for the spending and then marking as spent,
// but because we have the `UNIQUE` constraint on the database table
@@ -311,12 +380,21 @@ where
trace!("storing serial number information");
self.inner
.storage
.insert_spent_credential(serial_number, was_freepass, self.client.address)
.insert_spent_credential(
serial_number,
freepass_expiration.is_some(),
self.client.address,
)
.await?;
trace!("increasing client bandwidth");
self.increase_bandwidth(bandwidth).await?;
let available_total = self.get_available_bandwidth().await?;
// set free pass expiration
if let Some(expiration) = freepass_expiration {
self.set_freepass_expiration(expiration).await?;
}
let available_total = self.client_bandwidth.bandwidth.bytes;
Ok(ServerResponse::Bandwidth { available_total })
}
@@ -367,17 +445,47 @@ where
) -> Result<ServerResponse, RequestHandlingError> {
debug!("handling testnet bandwidth request");
if self.inner.only_coconut_credentials {
if self.inner.shared_state.only_coconut_credentials {
return Err(RequestHandlingError::OnlyCoconutCredentials);
}
self.increase_bandwidth(FREE_TESTNET_BANDWIDTH_VALUE)
.await?;
let available_total = self.get_available_bandwidth().await?;
let available_total = self.client_bandwidth.bandwidth.bytes;
Ok(ServerResponse::Bandwidth { available_total })
}
async fn flush_bandwidth(&mut self) -> Result<(), RequestHandlingError> {
trace!("flushing client bandwidth to the underlying storage");
self.inner
.storage
.set_bandwidth(self.client.address, self.client_bandwidth.bandwidth.bytes)
.await?;
self.client_bandwidth.update_flush_data();
Ok(())
}
async fn try_use_bandwidth(
&mut self,
required_bandwidth: i64,
) -> Result<i64, RequestHandlingError> {
if self.client_bandwidth.bandwidth.freepass_expired() {
self.expire_freepass().await?;
}
let available_bandwidth = self.client_bandwidth.bandwidth.bytes;
if available_bandwidth < required_bandwidth {
return Err(RequestHandlingError::OutOfBandwidth {
required: required_bandwidth,
available: available_bandwidth,
});
}
self.consume_bandwidth(required_bandwidth).await?;
Ok(self.client_bandwidth.bandwidth.bytes)
}
/// Tries to handle request to forward sphinx packet into the network. The request can only succeed
/// if the client has enough available bandwidth.
///
@@ -387,24 +495,16 @@ where
///
/// * `mix_packet`: packet received from the client that should get forwarded into the network.
async fn handle_forward_sphinx(
&self,
&mut self,
mix_packet: MixPacket,
) -> Result<ServerResponse, RequestHandlingError> {
let consumed_bandwidth = mix_packet.packet().len() as i64;
let required_bandwidth = mix_packet.packet().len() as i64;
let available_bandwidth = self.get_available_bandwidth().await?;
if available_bandwidth < consumed_bandwidth {
return Ok(ServerResponse::new_error(
"Insufficient bandwidth available",
));
}
self.consume_bandwidth(consumed_bandwidth).await?;
let remaining_bandwidth = self.try_use_bandwidth(required_bandwidth).await?;
self.forward_packet(mix_packet);
Ok(ServerResponse::Send {
remaining_bandwidth: available_bandwidth - consumed_bandwidth,
remaining_bandwidth,
})
}
@@ -413,7 +513,7 @@ where
/// # Arguments
///
/// * `bin_msg`: raw message to handle.
async fn handle_binary(&self, bin_msg: Vec<u8>) -> Message {
async fn handle_binary(&mut self, bin_msg: Vec<u8>) -> Message {
trace!("binary request");
// this function decrypts the request and checks the MAC
match BinaryRequest::try_from_encrypted_tagged_bytes(bin_msg, &self.client.shared_keys) {
@@ -455,7 +555,13 @@ where
.handle_claim_testnet_bandwidth()
.await
.into_ws_message(),
_ => RequestHandlingError::IllegalRequest.into_error_message(),
other => RequestHandlingError::IllegalRequest {
additional_context: format!(
"received illegal message of type {} in an authenticated client",
other.name()
),
}
.into_error_message(),
},
}
}
@@ -1,6 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::RequestHandlingError;
use futures::{
channel::{mpsc, oneshot},
SinkExt, StreamExt,
@@ -20,18 +21,19 @@ use nym_gateway_requests::{
use nym_mixnet_client::forwarder::MixForwardingSender;
use nym_sphinx::DestinationAddressBytes;
use rand::{CryptoRng, Rng};
use std::{sync::Arc, time::Duration};
use std::time::Duration;
use thiserror::Error;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
use crate::node::client_handling::websocket::common_state::CommonHandlerState;
use crate::node::client_handling::websocket::connection_handler::AvailableBandwidth;
use crate::node::{
client_handling::{
active_clients::ActiveClientsStore,
websocket::{
connection_handler::{
coconut::CoconutVerifier, AuthenticatedHandler, ClientDetails, InitialAuthResult,
SocketStream,
AuthenticatedHandler, ClientDetails, InitialAuthResult, SocketStream,
},
message_receiver::{IsActive, IsActiveRequestSender},
},
@@ -77,24 +79,55 @@ pub(crate) enum InitialAuthenticationError {
#[error("Attempted to negotiate connection with client using incompatible protocol version. Ours is {current} and the client reports {client:?}")]
IncompatibleProtocol { client: Option<u8>, current: u8 },
#[error("failed to send authentication error response: {source}")]
ErrorResponseSendFailure {
#[source]
source: WsError,
},
#[error("failed to send authentication response: {source}")]
ResponseSendFailure {
#[source]
source: WsError,
},
#[error("possibly received a sphinx packet without prior authentication. Request is going to be ignored")]
BinaryRequestWithoutAuthentication,
#[error("received a connection close message")]
CloseMessage,
#[error("the connection has unexpectedly closed")]
ClosedConnection,
#[error("failed to obtain message from websocket stream: {source}")]
FailedToReadMessage {
#[source]
source: WsError,
},
#[error("could not establish client details")]
EmptyClientDetails,
#[error("failed to upgrade the client handler: {source}")]
HandlerUpgradeFailure { source: RequestHandlingError },
}
impl InitialAuthenticationError {
/// Converts this Error into an appropriate websocket Message.
fn into_error_message(self) -> Message {
fn to_error_message(&self) -> Message {
ServerResponse::new_error(self.to_string()).into()
}
}
pub(crate) struct FreshHandler<R, S, St> {
rng: R,
local_identity: Arc<identity::KeyPair>,
pub(crate) only_coconut_credentials: bool,
pub(crate) shared_state: CommonHandlerState,
pub(crate) active_clients_store: ActiveClientsStore,
pub(crate) outbound_mix_sender: MixForwardingSender,
pub(crate) socket_connection: SocketStream<S>,
pub(crate) storage: St,
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
// currently unused (but populated)
pub(crate) negotiated_protocol: Option<u8>,
@@ -113,23 +146,19 @@ where
pub(crate) fn new(
rng: R,
conn: S,
only_coconut_credentials: bool,
outbound_mix_sender: MixForwardingSender,
local_identity: Arc<identity::KeyPair>,
storage: St,
active_clients_store: ActiveClientsStore,
coconut_verifier: Arc<CoconutVerifier>,
shared_state: CommonHandlerState,
) -> Self {
FreshHandler {
rng,
active_clients_store,
only_coconut_credentials,
outbound_mix_sender,
socket_connection: SocketStream::RawTcp(conn),
local_identity,
storage,
coconut_verifier,
negotiated_protocol: None,
shared_state,
}
}
@@ -171,7 +200,7 @@ where
gateway_handshake(
&mut self.rng,
ws_stream,
self.local_identity.as_ref(),
self.shared_state.local_identity.as_ref(),
init_msg,
)
.await
@@ -436,7 +465,7 @@ where
// Ask the other connection to ping if they are still active.
// Use a oneshot channel to return the result to us
let (ping_result_sender, ping_result_receiver) = oneshot::channel();
log::debug!("Asking other connection handler to ping the connected client to see if it is still active");
debug!("Asking other connection handler to ping the connected client to see if it is still active");
if let Err(err) = is_active_request_tx.send(ping_result_sender).await {
warn!("Failed to send ping request to other handler: {err}");
}
@@ -448,31 +477,31 @@ where
IsActive::NotActive => {
// The other handler reported that the client is not active, so we can
// disconnect the other client and continue with this connection.
log::debug!("Other handler reports it is not active");
debug!("Other handler reports it is not active");
self.active_clients_store.disconnect(address);
}
IsActive::Active => {
// The other handled reported a positive reply, so we have to assume it's
// still active and disconnect this connection.
log::info!("Other handler reports it is active");
info!("Other handler reports it is active");
return Err(InitialAuthenticationError::DuplicateConnection);
}
IsActive::BusyPinging => {
// The other handler is already busy pinging the client, so we have to
// assume it's still active and disconnect this connection.
log::debug!("Other handler reports it is already busy pinging the client");
debug!("Other handler reports it is already busy pinging the client");
return Err(InitialAuthenticationError::DuplicateConnection);
}
}
}
Ok(Err(_)) => {
// Other channel failed to reply (the channel sender probably dropped)
log::info!("Other connection failed to reply, disconnecting it in favour of this new connection");
info!("Other connection failed to reply, disconnecting it in favour of this new connection");
self.active_clients_store.disconnect(address);
}
Err(_) => {
// Timeout waiting for reply
log::warn!(
warn!(
"Other connection timed out, disconnecting it in favour of this new connection"
);
self.active_clients_store.disconnect(address);
@@ -509,7 +538,7 @@ where
// Check for duplicate clients
if let Some(client_tx) = self.active_clients_store.get_remote_client(address) {
log::warn!("Detected duplicate connection for client: {address}");
warn!("Detected duplicate connection for client: {address}");
self.handle_duplicate_client(address, client_tx.is_active_request_sender)
.await?;
}
@@ -518,11 +547,17 @@ where
.authenticate_client(address, encrypted_address, iv)
.await?;
let status = shared_keys.is_some();
let bandwidth_remaining = self
.storage
.get_available_bandwidth(address)
.await?
.unwrap_or(0);
let available_bandwidth: AvailableBandwidth =
self.storage.get_available_bandwidth(address).await?.into();
let bandwidth_remaining = if available_bandwidth.freepass_expired() {
self.expire_freepass(address).await?;
0
} else {
available_bandwidth.bytes
};
let client_details =
shared_keys.map(|shared_keys| ClientDetails::new(address, shared_keys));
@@ -536,6 +571,13 @@ where
))
}
pub(crate) async fn expire_freepass(
&self,
client: DestinationAddressBytes,
) -> Result<(), StorageError> {
self.storage.reset_freepass_bandwidth(client).await
}
/// Attempts to finalize registration of the client by storing the derived shared keys in the
/// persistent store as well as creating entry for its bandwidth allocation.
///
@@ -657,7 +699,7 @@ where
// TODO: somehow cleanup this method
pub(crate) async fn perform_initial_authentication(
mut self,
) -> Option<AuthenticatedHandler<R, S, St>>
) -> Result<AuthenticatedHandler<R, S, St>, InitialAuthenticationError>
where
S: AsyncRead + AsyncWrite + Unpin + Send,
{
@@ -666,40 +708,51 @@ where
while let Some(msg) = self.read_websocket_message().await {
let msg = match msg {
Ok(msg) => msg,
Err(err) => {
debug!("failed to obtain message from websocket stream! stopping connection handler: {err}");
break;
Err(source) => {
debug!("failed to obtain message from websocket stream! stopping connection handler: {source}");
return Err(InitialAuthenticationError::FailedToReadMessage { source });
}
};
if msg.is_close() {
break;
return Err(InitialAuthenticationError::CloseMessage);
}
// ONLY handle 'Authenticate' or 'Register' requests, ignore everything else
match msg {
Message::Close(_) => break,
// we have explicitly checked for close message
Message::Close(_) => unreachable!(),
Message::Text(text_msg) => {
let (mix_sender, mix_receiver) = mpsc::unbounded();
match self.handle_initial_authentication_request(text_msg).await {
return match self.handle_initial_authentication_request(text_msg).await {
Err(err) => {
if let Err(err) =
self.send_websocket_message(err.into_error_message()).await
debug!("authentication failure: {err}");
// try to send error to the client
if let Err(source) =
self.send_websocket_message(err.to_error_message()).await
{
debug!("Failed to send authentication error response - {err}");
return None;
debug!("Failed to send authentication error response: {source}");
return Err(InitialAuthenticationError::ErrorResponseSendFailure {
source,
});
}
// return the underlying error
Err(err)
}
Ok(auth_result) => {
if let Err(err) = self
// try to send auth response back to the client
if let Err(source) = self
.send_websocket_message(auth_result.server_response.into())
.await
{
debug!("Failed to send authentication response - {err}");
return None;
debug!("Failed to send authentication response: {source}");
return Err(InitialAuthenticationError::ResponseSendFailure {
source,
});
}
return if let Some(client_details) = auth_result.client_details {
if let Some(client_details) = auth_result.client_details {
// Channel for handlers to ask other handlers if they are still active.
let (is_active_request_sender, is_active_request_receiver) =
mpsc::unbounded();
@@ -708,23 +761,29 @@ where
mix_sender,
is_active_request_sender,
);
Some(AuthenticatedHandler::upgrade(
AuthenticatedHandler::upgrade(
self,
client_details,
mix_receiver,
is_active_request_receiver,
))
)
.await
.map_err(|source| {
InitialAuthenticationError::HandlerUpgradeFailure { source }
})
} else {
None
};
// honestly, it's been so long I don't remember under what conditions its possible (if at all)
// to have empty client details
Err(InitialAuthenticationError::EmptyClientDetails)
}
}
}
};
}
Message::Binary(_) => {
// perhaps logging level should be reduced here, let's leave it for now and see what happens
// if client is working correctly, this should have never happened
debug!("possibly received a sphinx packet without prior authentication. Request is going to be ignored");
if let Err(err) = self
if let Err(source) = self
.send_websocket_message(
ServerResponse::new_error(
"binary request without prior authentication",
@@ -733,15 +792,18 @@ where
)
.await
{
debug!("Failed to send error response during authentication: {err}",)
return Err(InitialAuthenticationError::ErrorResponseSendFailure {
source,
});
}
return None;
return Err(InitialAuthenticationError::BinaryRequestWithoutAuthentication);
}
_ => continue,
};
}
None
Err(InitialAuthenticationError::ClosedConnection)
}
pub(crate) async fn start_handling(self, shutdown: nym_task::TaskClient)
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::Config;
use crate::node::storage::Storage;
use log::{trace, warn};
use nym_gateway_requests::registration::handshake::SharedKeys;
@@ -8,6 +9,8 @@ use nym_gateway_requests::ServerResponse;
use nym_sphinx::DestinationAddressBytes;
use nym_task::TaskClient;
use rand::{CryptoRng, Rng};
use std::time::Duration;
use time::OffsetDateTime;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::WebSocketStream;
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -103,12 +106,84 @@ pub(crate) async fn handle_connection<R, S, St>(
trace!("received shutdown signal while performing initial authentication");
return;
}
Some(None) => {
warn!("authentication has failed");
Some(Err(err)) => {
warn!("authentication has failed: {err}");
return;
}
Some(Some(auth_handle)) => auth_handle.listen_for_requests(shutdown).await,
Some(Ok(auth_handle)) => auth_handle.listen_for_requests(shutdown).await,
}
trace!("The handler is done!");
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct BandwidthFlushingBehaviourConfig {
/// Defines maximum delay between client bandwidth information being flushed to the persistent storage.
pub client_bandwidth_max_flushing_rate: Duration,
/// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage.
pub client_bandwidth_max_delta_flushing_amount: i64,
}
impl<'a> From<&'a Config> for BandwidthFlushingBehaviourConfig {
fn from(value: &'a Config) -> Self {
BandwidthFlushingBehaviourConfig {
client_bandwidth_max_flushing_rate: value.debug.client_bandwidth_max_flushing_rate,
client_bandwidth_max_delta_flushing_amount: value
.debug
.client_bandwidth_max_delta_flushing_amount,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct AvailableBandwidth {
pub(crate) bytes: i64,
pub(crate) freepass_expiration: Option<OffsetDateTime>,
}
impl AvailableBandwidth {
pub(crate) fn freepass_expired(&self) -> bool {
if let Some(expiration) = self.freepass_expiration {
if expiration < OffsetDateTime::now_utc() {
return true;
}
}
false
}
}
pub(crate) struct ClientBandwidth {
pub(crate) bandwidth: AvailableBandwidth,
pub(crate) last_flushed: OffsetDateTime,
pub(crate) bytes_at_last_flush: i64,
}
impl ClientBandwidth {
pub(crate) fn new(bandwidth: AvailableBandwidth) -> ClientBandwidth {
ClientBandwidth {
bandwidth,
last_flushed: OffsetDateTime::now_utc(),
bytes_at_last_flush: bandwidth.bytes,
}
}
pub(crate) fn should_flush(&self, cfg: BandwidthFlushingBehaviourConfig) -> bool {
if (self.bytes_at_last_flush - self.bandwidth.bytes).abs()
>= cfg.client_bandwidth_max_delta_flushing_amount
{
return true;
}
if self.last_flushed + cfg.client_bandwidth_max_flushing_rate < OffsetDateTime::now_utc() {
return true;
}
false
}
pub(crate) fn update_flush_data(&mut self) {
self.last_flushed = OffsetDateTime::now_utc();
self.bytes_at_last_flush = self.bandwidth.bytes;
}
}
@@ -2,37 +2,26 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::active_clients::ActiveClientsStore;
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
use crate::node::client_handling::websocket::common_state::CommonHandlerState;
use crate::node::client_handling::websocket::connection_handler::FreshHandler;
use crate::node::storage::Storage;
use log::*;
use nym_crypto::asymmetric::identity;
use nym_mixnet_client::forwarder::MixForwardingSender;
use rand::rngs::OsRng;
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
use tokio::task::JoinHandle;
pub(crate) struct Listener {
address: SocketAddr,
local_identity: Arc<identity::KeyPair>,
only_coconut_credentials: bool,
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
shared_state: CommonHandlerState,
}
impl Listener {
pub(crate) fn new(
address: SocketAddr,
local_identity: Arc<identity::KeyPair>,
only_coconut_credentials: bool,
coconut_verifier: Arc<CoconutVerifier>,
) -> Self {
pub(crate) fn new(address: SocketAddr, shared_state: CommonHandlerState) -> Self {
Listener {
address,
local_identity,
only_coconut_credentials,
coconut_verifier,
shared_state,
}
}
@@ -71,12 +60,10 @@ impl Listener {
let handle = FreshHandler::new(
OsRng,
socket,
self.only_coconut_credentials,
outbound_mix_sender.clone(),
Arc::clone(&self.local_identity),
storage.clone(),
active_clients_store.clone(),
Arc::clone(&self.coconut_verifier),
self.shared_state.clone(),
);
let shutdown = shutdown.clone().named(format!("ClientConnectionHandler_{remote_addr}"));
tokio::spawn(async move { handle.start_handling(shutdown).await });
@@ -3,6 +3,9 @@
pub(crate) use listener::Listener;
pub(crate) mod common_state;
pub(crate) mod connection_handler;
pub(crate) mod listener;
pub(crate) mod message_receiver;
pub(crate) use common_state::CommonHandlerState;
+9 -8
View File
@@ -150,7 +150,7 @@ impl<St> Gateway<St> {
network_requester_opts,
ip_packet_router_opts,
client_registry: Arc::new(DashMap::new()),
run_http_server: false,
run_http_server: true,
task_client: None,
})
}
@@ -171,7 +171,7 @@ impl<St> Gateway<St> {
sphinx_keypair,
storage,
client_registry: Arc::new(DashMap::new()),
run_http_server: false,
run_http_server: true,
task_client: None,
}
}
@@ -254,13 +254,14 @@ impl<St> Gateway<St> {
self.config.gateway.clients_port,
);
websocket::Listener::new(
listening_address,
Arc::clone(&self.identity_keypair),
self.config.gateway.only_coconut_credentials,
let shared_state = websocket::CommonHandlerState {
coconut_verifier,
)
.start(
local_identity: Arc::clone(&self.identity_keypair),
only_coconut_credentials: self.config.gateway.only_coconut_credentials,
bandwidth_cfg: (&self.config).into(),
};
websocket::Listener::new(listening_address, shared_state).start(
forwarding_channel,
self.storage.clone(),
active_clients_store,
+57 -37
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::storage::models::{PersistedBandwidth, SpentCredential};
use time::OffsetDateTime;
#[derive(Clone)]
pub(crate) struct BandwidthManager {
@@ -36,6 +37,53 @@ impl BandwidthManager {
Ok(())
}
/// Set the freepass expiration date of the particular client to the provided date.
///
/// # Arguments
///
/// * `client_address_bs58`: base58-encoded address of the client.
/// * `freepass_expiration`: the expiration date of the associated free pass.
pub(crate) async fn set_freepass_expiration(
&self,
client_address_bs58: &str,
freepass_expiration: OffsetDateTime,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
UPDATE available_bandwidth
SET freepass_expiration = ?
WHERE client_address_bs58 = ?
"#,
freepass_expiration,
client_address_bs58
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
/// Reset all the bandwidth associated with the freepass and reset its expiration date
///
/// # Arguments
///
/// * `client_address_bs58`: base58-encoded address of the client.
pub(crate) async fn reset_freepass_bandwidth(
&self,
client_address_bs58: &str,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
UPDATE available_bandwidth
SET available = 0, freepass_expiration = NULL
WHERE client_address_bs58 = ?
"#,
client_address_bs58
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
/// Tries to retrieve available bandwidth for the particular client.
///
/// # Arguments
@@ -45,22 +93,19 @@ impl BandwidthManager {
&self,
client_address_bs58: &str,
) -> Result<Option<PersistedBandwidth>, sqlx::Error> {
sqlx::query_as!(
PersistedBandwidth,
"SELECT * FROM available_bandwidth WHERE client_address_bs58 = ?",
client_address_bs58
)
.fetch_optional(&self.connection_pool)
.await
sqlx::query_as("SELECT * FROM available_bandwidth WHERE client_address_bs58 = ?")
.bind(client_address_bs58)
.fetch_optional(&self.connection_pool)
.await
}
/// Increases available bandwidth of the particular client by the specified amount.
/// Sets available bandwidth of the particular client to the provided amount;
///
/// # Arguments
///
/// * `client_address_bs58`: base58-encoded address of the client.
/// * `amount`: amount of available bandwidth to be added to the client.
pub(crate) async fn increase_available_bandwidth(
/// * `client_address`: address of the client
/// * `amount`: the updated client bandwidth amount.
pub(crate) async fn set_available_bandwidth(
&self,
client_address_bs58: &str,
amount: i64,
@@ -68,32 +113,7 @@ impl BandwidthManager {
sqlx::query!(
r#"
UPDATE available_bandwidth
SET available = available + ?
WHERE client_address_bs58 = ?
"#,
amount,
client_address_bs58
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
/// Decreases available bandwidth of the particular client by the specified amount.
///
/// # Arguments
///
/// * `client_address_bs58`: base58-encoded address of the client.
/// * `amount`: amount of available bandwidth to be removed from the client.
pub(crate) async fn decrease_available_bandwidth(
&self,
client_address_bs58: &str,
amount: i64,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
UPDATE available_bandwidth
SET available = available - ?
SET available = ?
WHERE client_address_bs58 = ?
"#,
amount,
+70 -44
View File
@@ -4,7 +4,7 @@
use crate::node::storage::bandwidth::BandwidthManager;
use crate::node::storage::error::StorageError;
use crate::node::storage::inboxes::InboxManager;
use crate::node::storage::models::{PersistedSharedKeys, StoredMessage};
use crate::node::storage::models::{PersistedBandwidth, PersistedSharedKeys, StoredMessage};
use crate::node::storage::shared_keys::SharedKeysManager;
use async_trait::async_trait;
use log::{debug, error};
@@ -13,6 +13,7 @@ use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::DestinationAddressBytes;
use sqlx::ConnectOptions;
use std::path::Path;
use time::OffsetDateTime;
mod bandwidth;
pub(crate) mod error;
@@ -102,6 +103,28 @@ pub trait Storage: Send + Sync {
client_address: DestinationAddressBytes,
) -> Result<(), StorageError>;
/// Set the freepass expiration date of the particular client to the provided date.
///
/// # Arguments
///
/// * `client_address`: address of the client
/// * `freepass_expiration`: the expiration date of the associated free pass.
async fn set_freepass_expiration(
&self,
client_address: DestinationAddressBytes,
freepass_expiration: OffsetDateTime,
) -> Result<(), StorageError>;
/// Reset all the bandwidth associated with the freepass and reset its expiration date
///
/// # Arguments
///
/// * `client_address`: address of the client
async fn reset_freepass_bandwidth(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), StorageError>;
/// Tries to retrieve available bandwidth for the particular client.
///
/// # Arguments
@@ -110,27 +133,15 @@ pub trait Storage: Send + Sync {
async fn get_available_bandwidth(
&self,
client_address: DestinationAddressBytes,
) -> Result<Option<i64>, StorageError>;
) -> Result<Option<PersistedBandwidth>, StorageError>;
/// Increases available bandwidth of the particular client by the specified amount.
/// Sets available bandwidth of the particular client to the provided amount;
///
/// # Arguments
///
/// * `client_address`: address of the client
/// * `amount`: amount of available bandwidth to be added to the client.
async fn increase_bandwidth(
&self,
client_address: DestinationAddressBytes,
amount: i64,
) -> Result<(), StorageError>;
/// Decreases available bandwidth of the particular client by the specified amount.
///
/// # Arguments
///
/// * `client_address`: address of the client
/// * `amount`: amount of available bandwidth to be removed from the client.
async fn consume_bandwidth(
/// * `amount`: the updated client bandwidth amount.
async fn set_bandwidth(
&self,
client_address: DestinationAddressBytes,
amount: i64,
@@ -295,36 +306,44 @@ impl Storage for PersistentStorage {
Ok(())
}
async fn get_available_bandwidth(
async fn set_freepass_expiration(
&self,
client_address: DestinationAddressBytes,
) -> Result<Option<i64>, StorageError> {
let res = self
.bandwidth_manager
.get_available_bandwidth(&client_address.as_base58_string())
.await
.map(|bandwidth_option| bandwidth_option.map(|bandwidth| bandwidth.available))?;
Ok(res)
}
async fn increase_bandwidth(
&self,
client_address: DestinationAddressBytes,
amount: i64,
freepass_expiration: OffsetDateTime,
) -> Result<(), StorageError> {
self.bandwidth_manager
.increase_available_bandwidth(&client_address.as_base58_string(), amount)
.set_freepass_expiration(&client_address.as_base58_string(), freepass_expiration)
.await?;
Ok(())
}
async fn consume_bandwidth(
async fn reset_freepass_bandwidth(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), StorageError> {
self.bandwidth_manager
.reset_freepass_bandwidth(&client_address.as_base58_string())
.await?;
Ok(())
}
async fn get_available_bandwidth(
&self,
client_address: DestinationAddressBytes,
) -> Result<Option<PersistedBandwidth>, StorageError> {
Ok(self
.bandwidth_manager
.get_available_bandwidth(&client_address.as_base58_string())
.await?)
}
async fn set_bandwidth(
&self,
client_address: DestinationAddressBytes,
amount: i64,
) -> Result<(), StorageError> {
self.bandwidth_manager
.decrease_available_bandwidth(&client_address.as_base58_string(), amount)
.set_available_bandwidth(&client_address.as_base58_string(), amount)
.await?;
Ok(())
}
@@ -422,22 +441,29 @@ impl Storage for InMemStorage {
todo!()
}
async fn get_available_bandwidth(
async fn set_freepass_expiration(
&self,
_client_address: DestinationAddressBytes,
) -> Result<Option<i64>, StorageError> {
todo!()
}
async fn increase_bandwidth(
&self,
_client_address: DestinationAddressBytes,
_amount: i64,
_freepass_expiration: OffsetDateTime,
) -> Result<(), StorageError> {
todo!()
}
async fn consume_bandwidth(
async fn reset_freepass_bandwidth(
&self,
_client_address: DestinationAddressBytes,
) -> Result<(), StorageError> {
todo!()
}
async fn get_available_bandwidth(
&self,
_client_address: DestinationAddressBytes,
) -> Result<Option<PersistedBandwidth>, StorageError> {
todo!()
}
async fn set_bandwidth(
&self,
_client_address: DestinationAddressBytes,
_amount: i64,
+22
View File
@@ -1,7 +1,9 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::websocket::connection_handler::AvailableBandwidth;
use sqlx::FromRow;
use time::OffsetDateTime;
pub struct PersistedSharedKeys {
pub(crate) client_address_bs58: String,
@@ -15,10 +17,30 @@ pub struct StoredMessage {
pub(crate) content: Vec<u8>,
}
#[derive(Debug, Clone, FromRow)]
pub struct PersistedBandwidth {
#[allow(dead_code)]
pub(crate) client_address_bs58: String,
pub(crate) available: i64,
pub(crate) freepass_expiration: Option<OffsetDateTime>,
}
impl From<PersistedBandwidth> for AvailableBandwidth {
fn from(value: PersistedBandwidth) -> Self {
AvailableBandwidth {
bytes: value.available,
freepass_expiration: value.freepass_expiration,
}
}
}
impl From<Option<PersistedBandwidth>> for AvailableBandwidth {
fn from(value: Option<PersistedBandwidth>) -> Self {
match value {
None => AvailableBandwidth::default(),
Some(b) => b.into(),
}
}
}
#[derive(Debug, Clone, FromRow)]
+1 -1
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-mixnode"
license = "GPL-3.0"
version = "1.1.35"
version = "1.1.37"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+2
View File
@@ -1,6 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
#![allow(unused)]
use crate::node::http::legacy::description::description;
use crate::node::http::legacy::hardware::hardware;
use crate::node::http::legacy::state::MixnodeAppState;
+1 -1
View File
@@ -105,7 +105,7 @@ impl<'a> HttpApiBuilder<'a> {
let router = nym_node_http_api::NymNodeRouter::new(config, None, None);
let server = router
.with_merged(legacy::routes(self.legacy_mixnode, self.legacy_descriptor))
// .with_merged(legacy::routes(self.legacy_mixnode, self.legacy_descriptor))
.build_server(&bind_address)?
.with_task_client(task_client);
tokio::spawn(async move { server.run().await });
+2 -2
View File
@@ -46,7 +46,7 @@ pub struct MixNode {
impl MixNode {
pub fn new(config: Config) -> Result<Self, MixnodeError> {
Ok(MixNode {
run_http_server: false,
run_http_server: true,
descriptor: Self::load_node_description(&config),
identity_keypair: Arc::new(load_identity_keys(&config)?),
sphinx_keypair: Arc::new(load_sphinx_keys(&config)?),
@@ -64,7 +64,7 @@ impl MixNode {
sphinx_keypair: Arc<encryption::KeyPair>,
) -> Self {
MixNode {
run_http_server: false,
run_http_server: true,
task_client: None,
config,
descriptor,
+1 -1
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-api"
license = "GPL-3.0"
version = "1.1.35"
version = "1.1.37"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+3 -1
View File
@@ -15,12 +15,14 @@ schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"] }
ts-rs = { workspace = true, optional = true }
tendermint = { workspace = true }
time = { workspace = true, features = ["serde", "parsing", "formatting"] }
# for serde on secp256k1 signatures
ecdsa = { version = "0.16", features = ["serde"] }
nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-crypto = { path = "../../common/crypto", features = ["serde", "asymmetric"]}
nym-crypto = { path = "../../common/crypto", features = ["serde", "asymmetric"] }
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
nym-node-requests = { path = "../../nym-node/nym-node-requests", default-features = false }
+155 -2
View File
@@ -9,12 +9,16 @@ use nym_mixnet_contract_common::rewarding::RewardEstimate;
use nym_mixnet_contract_common::{
GatewayBond, IdentityKey, Interval, MixId, MixNode, Percent, RewardedSetNodeStatus,
};
use nym_node_requests::api::v1::gateway::models::WebSockets;
use nym_node_requests::api::v1::node::models::{BinaryBuildInformationOwned, HostInformation};
use nym_node_requests::api::v1::node::models::BinaryBuildInformationOwned;
use schemars::gen::SchemaGenerator;
use schemars::schema::{InstanceType, Schema, SchemaObject};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::net::IpAddr;
use std::ops::{Deref, DerefMut};
use std::{fmt, time::Duration};
use time::OffsetDateTime;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
pub struct RequestError {
@@ -356,8 +360,119 @@ pub struct CirculatingSupplyResponse {
pub circulating_supply: Coin,
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct HostInformation {
pub ip_address: Vec<IpAddr>,
pub hostname: Option<String>,
pub keys: HostKeys,
}
impl From<nym_node_requests::api::v1::node::models::HostInformation> for HostInformation {
fn from(value: nym_node_requests::api::v1::node::models::HostInformation) -> Self {
HostInformation {
ip_address: value.ip_address,
hostname: value.hostname,
keys: value.keys.into(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct HostKeys {
pub ed25519: String,
pub x25519: String,
}
impl From<nym_node_requests::api::v1::node::models::HostKeys> for HostKeys {
fn from(value: nym_node_requests::api::v1::node::models::HostKeys) -> Self {
HostKeys {
ed25519: value.ed25519_identity,
x25519: value.x25519_sphinx,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct WebSockets {
pub ws_port: u16,
pub wss_port: Option<u16>,
}
impl From<nym_node_requests::api::v1::gateway::models::WebSockets> for WebSockets {
fn from(value: nym_node_requests::api::v1::gateway::models::WebSockets) -> Self {
WebSockets {
ws_port: value.ws_port,
wss_port: value.wss_port,
}
}
}
const fn unix_epoch() -> OffsetDateTime {
OffsetDateTime::UNIX_EPOCH
}
// for all intents and purposes it's just OffsetDateTime, but we need JsonSchema...
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct OffsetDateTimeJsonSchemaWrapper(#[serde(default = "unix_epoch")] pub OffsetDateTime);
impl Default for OffsetDateTimeJsonSchemaWrapper {
fn default() -> Self {
OffsetDateTimeJsonSchemaWrapper(unix_epoch())
}
}
impl From<OffsetDateTimeJsonSchemaWrapper> for OffsetDateTime {
fn from(value: OffsetDateTimeJsonSchemaWrapper) -> Self {
value.0
}
}
impl From<OffsetDateTime> for OffsetDateTimeJsonSchemaWrapper {
fn from(value: OffsetDateTime) -> Self {
OffsetDateTimeJsonSchemaWrapper(value)
}
}
impl Deref for OffsetDateTimeJsonSchemaWrapper {
type Target = OffsetDateTime;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for OffsetDateTimeJsonSchemaWrapper {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
// implementation taken from: https://github.com/GREsau/schemars/pull/207
impl JsonSchema for OffsetDateTimeJsonSchemaWrapper {
fn is_referenceable() -> bool {
false
}
fn schema_name() -> String {
"DateTime".into()
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
format: Some("date-time".into()),
..Default::default()
}
.into()
}
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct NymNodeDescription {
#[serde(default)]
pub last_polled: OffsetDateTimeJsonSchemaWrapper,
pub host_information: HostInformation,
// TODO: do we really care about ALL build info or just the version?
@@ -402,3 +517,41 @@ pub struct IpPacketRouterDetails {
/// address of the embedded ip packet router
pub address: String,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct ApiHealthResponse {
pub status: ApiStatus,
pub uptime: u64,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum ApiStatus {
Up,
}
impl ApiHealthResponse {
pub fn new_healthy(uptime: Duration) -> Self {
ApiHealthResponse {
status: ApiStatus::Up,
uptime: uptime.as_secs(),
}
}
}
impl ApiStatus {
pub fn is_up(&self) -> bool {
matches!(self, ApiStatus::Up)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SignerInformationResponse {
pub cosmos_address: String,
pub identity: String,
pub announce_address: String,
pub verification_key: Option<String>,
}
+4
View File
@@ -67,6 +67,10 @@ fn validate_freepass_public_attributes(res: &FreePassRequest) -> Result<()> {
return Err(CoconutError::TooLongFreePass { expiry_date });
}
if expiry_date < now {
return Err(CoconutError::FreePassExpiryInThePast { expiry_date });
}
Ok(())
}
+3
View File
@@ -82,6 +82,9 @@ pub enum CoconutError {
)]
TooLongFreePass { expiry_date: OffsetDateTime },
#[error("the provided free pass expiry is set in the past!")]
FreePassExpiryInThePast { expiry_date: OffsetDateTime },
#[error("the received bandwidth voucher did not contain deposit value")]
MissingBandwidthValue,
+8
View File
@@ -1,6 +1,7 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_coconut::VerificationKey;
use nym_coconut_dkg_common::types::EpochId;
use nym_dkg::Scalar;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -63,6 +64,13 @@ impl KeyPair {
}
}
pub async fn verification_key(&self) -> Option<RwLockReadGuard<'_, VerificationKey>> {
RwLockReadGuard::try_map(self.get().await?, |maybe_keypair| {
maybe_keypair.as_ref().map(|k| k.keys.verification_key())
})
.ok()
}
pub async fn read_keys(&self) -> RwLockReadGuard<'_, Option<KeyPairWithEpoch>> {
self.keys.read().await
}
+1
View File
@@ -38,6 +38,7 @@ pub(crate) mod node_describe_cache;
pub(crate) mod node_status_api;
pub(crate) mod nym_contract_cache;
pub(crate) mod nym_nodes;
mod status;
pub(crate) mod support;
struct ShutdownHandles {
+7 -5
View File
@@ -16,6 +16,7 @@ use nym_mixnet_contract_common::Gateway;
use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt};
use std::collections::HashMap;
use thiserror::Error;
use time::OffsetDateTime;
// type alias for ease of use
pub type DescribedNodes = HashMap<IdentityKey, NymNodeDescription>;
@@ -181,11 +182,12 @@ async fn get_gateway_description(
};
let description = NymNodeDescription {
host_information: host_info.data,
host_information: host_info.data.into(),
last_polled: OffsetDateTime::now_utc().into(),
build_information: build_info,
network_requester,
ip_packet_router,
mixnet_websockets: websockets,
mixnet_websockets: websockets.into(),
};
Ok((gateway.identity_key, description))
@@ -211,7 +213,7 @@ impl CacheItemProvider for NodeDescriptionProvider {
}
// TODO: somehow bypass the 'higher-ranked lifetime error' and remove that redundant clone
let websockets = stream::iter(
let node_description = stream::iter(
gateways
// .deref()
// .clone()
@@ -224,7 +226,7 @@ impl CacheItemProvider for NodeDescriptionProvider {
match res {
Ok((identity, description)) => Some((identity, description)),
Err(err) => {
debug!("{err}");
debug!("failed to obtain gateway self-described data: {err}");
None
}
}
@@ -232,7 +234,7 @@ impl CacheItemProvider for NodeDescriptionProvider {
.collect::<HashMap<_, _>>()
.await;
Ok(websockets)
Ok(node_description)
}
}
+53
View File
@@ -0,0 +1,53 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::coconut;
use nym_bin_common::bin_info;
use nym_bin_common::build_information::BinaryBuildInformation;
use okapi::openapi3::OpenApi;
use rocket::Route;
use rocket_okapi::openapi_get_routes_spec;
use rocket_okapi::settings::OpenApiSettings;
use tokio::time::Instant;
pub(crate) mod routes;
pub(crate) struct ApiStatusState {
startup_time: Instant,
build_information: BinaryBuildInformation,
signer_information: Option<SignerState>,
}
pub(crate) struct SignerState {
// static information
pub cosmos_address: String,
pub identity: String,
pub announce_address: String,
pub(crate) coconut_keypair: coconut::keys::KeyPair,
}
impl ApiStatusState {
pub fn new() -> Self {
ApiStatusState {
startup_time: Instant::now(),
build_information: bin_info!(),
signer_information: None,
}
}
pub fn add_zk_nym_signer(&mut self, signer_information: SignerState) {
self.signer_information = Some(signer_information)
}
}
pub(crate) fn api_status_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
settings:
routes::health,
routes::build_information,
routes::signer_information
]
}
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node_status_api::models::ErrorResponse;
use crate::status::ApiStatusState;
use nym_api_requests::models::{ApiHealthResponse, SignerInformationResponse};
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use nym_coconut::Base58;
use rocket::http::Status;
use rocket::serde::json::Json;
use rocket::State;
use rocket_okapi::openapi;
#[openapi(tag = "Api Status")]
#[get("/health")]
pub(crate) async fn health(state: &State<ApiStatusState>) -> Json<ApiHealthResponse> {
let uptime = state.startup_time.elapsed();
let health = ApiHealthResponse::new_healthy(uptime);
Json(health)
}
#[openapi(tag = "Api Status")]
#[get("/build-information")]
pub(crate) async fn build_information(
state: &State<ApiStatusState>,
) -> Json<BinaryBuildInformationOwned> {
Json(state.build_information.to_owned())
}
#[openapi(tag = "Api Status")]
#[get("/signer-information")]
pub(crate) async fn signer_information(
state: &State<ApiStatusState>,
) -> Result<Json<SignerInformationResponse>, ErrorResponse> {
let signer_state = state.signer_information.as_ref().ok_or_else(|| {
ErrorResponse::new(
"this api does not expose zk-nym signing functionalities",
Status::InternalServerError,
)
})?;
Ok(Json(SignerInformationResponse {
cosmos_address: signer_state.cosmos_address.clone(),
identity: signer_state.identity.clone(),
announce_address: signer_state.announce_address.clone(),
verification_key: signer_state
.coconut_keypair
.verification_key()
.await
.map(|maybe_vk| maybe_vk.to_bs58()),
}))
}
+24 -6
View File
@@ -9,6 +9,7 @@ use crate::network::network_routes;
use crate::node_describe_cache::DescribedNodes;
use crate::node_status_api::{self, NodeStatusCache};
use crate::nym_contract_cache::cache::NymContractCache;
use crate::status::{api_status_routes, ApiStatusState, SignerState};
use crate::support::caching::cache::SharedCache;
use crate::support::config::Config;
use crate::support::{nyxd, storage};
@@ -27,7 +28,7 @@ pub(crate) mod openapi;
pub(crate) async fn setup_rocket(
config: &Config,
network_details: NetworkDetails,
_nyxd_client: nyxd::Client,
nyxd_client: nyxd::Client,
identity_keypair: identity::KeyPair,
coconut_keypair: coconut::keys::KeyPair,
) -> anyhow::Result<Rocket<Ignite>> {
@@ -45,6 +46,7 @@ pub(crate) async fn setup_rocket(
"" => nym_contract_cache::nym_contract_cache_routes(&openapi_settings),
"/status" => node_status_api::node_status_routes(&openapi_settings, config.network_monitor.enabled),
"/network" => network_routes(&openapi_settings),
"/api-status" => api_status_routes(&openapi_settings),
"" => nym_node_routes(&openapi_settings),
}
@@ -68,18 +70,34 @@ pub(crate) async fn setup_rocket(
None
};
let mut status_state = ApiStatusState::new();
let rocket = if config.coconut_signer.enabled {
// make sure we have some tokens to cover multisig fees
let balance = _nyxd_client.balance(&mix_denom).await?;
let balance = nyxd_client.balance(&mix_denom).await?;
if balance.amount < coconut::MINIMUM_BALANCE {
let address = _nyxd_client.address().await;
let address = nyxd_client.address().await;
let min = Coin::new(coconut::MINIMUM_BALANCE, mix_denom);
bail!("the account ({address}) doesn't have enough funds to cover verification fees. it has {balance} while it needs at least {min}")
}
let comm_channel = QueryCommunicationChannel::new(_nyxd_client.clone());
let cosmos_address = nyxd_client.address().await.to_string();
let announce_address = config
.coconut_signer
.announce_address
.clone()
.map(|u| u.to_string())
.unwrap_or_default();
status_state.add_zk_nym_signer(SignerState {
cosmos_address,
identity: identity_keypair.public_key().to_base58_string(),
announce_address,
coconut_keypair: coconut_keypair.clone(),
});
let comm_channel = QueryCommunicationChannel::new(nyxd_client.clone());
rocket.attach(coconut::stage(
_nyxd_client.clone(),
nyxd_client.clone(),
mix_denom,
identity_keypair,
coconut_keypair,
@@ -97,7 +115,7 @@ pub(crate) async fn setup_rocket(
rocket
};
Ok(rocket.ignite().await?)
Ok(rocket.manage(status_state).ignite().await?)
}
fn setup_cors() -> Result<Cors> {
+3 -2
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-node"
version = "0.1.0"
version = "1.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -18,6 +18,7 @@ anyhow.workspace = true
bip39 = { workspace = true, features = ["zeroize"] }
bs58.workspace = true
celes = "2.4.0" # country codes
colored = "2"
clap = { workspace = true, features = ["cargo", "env"] }
humantime-serde = { workspace = true }
ipnetwork = "0.16.0"
@@ -58,4 +59,4 @@ nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
[build-dependencies]
# temporary bonding information v1 (to grab and parse nym-mixnode and nym-gateway package versions)
cargo_metadata = "0.18.1"
cargo_metadata = "0.18.1"
-36
View File
@@ -1,36 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use cargo_metadata::MetadataCommand;
use std::fs;
use std::path::PathBuf;
// that's disgusting, but it works, so it's good enough for now ¯\_(ツ)_/¯
fn main() {
let out_dir: PathBuf = std::env::var("OUT_DIR").unwrap().into();
let path: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();
let mix_path = path.parent().unwrap().join("mixnode");
let gateway_path = path.parent().unwrap().join("gateway");
let mix_meta = MetadataCommand::new()
.manifest_path("./Cargo.toml")
.current_dir(&mix_path)
.exec()
.unwrap();
let mix_version = &mix_meta.root_package().unwrap().version;
let gateway_meta = MetadataCommand::new()
.manifest_path("./Cargo.toml")
.current_dir(&gateway_path)
.exec()
.unwrap();
let gateway_version = &gateway_meta.root_package().unwrap().version;
fs::write(out_dir.join("mixnode_version"), mix_version.to_string()).unwrap();
fs::write(out_dir.join("gateway_version"), gateway_version.to_string()).unwrap();
println!("cargo::rerun-if-changed=build.rs");
}
+3
View File
@@ -36,6 +36,9 @@ nym-bin-common = { path = "../../common/bin-common", features = ["bin_info_schem
[dev-dependencies]
tokio = { workspace = true, features = ["full"] }
rand_chacha = "0.2"
nym-crypto = { path = "../../common/crypto", features = ["rand"] }
[features]
default = ["client"]
+79 -2
View File
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::api::v1::node::models::HostInformation;
use crate::api::v1::node::models::{HostInformation, LegacyHostInformation};
use crate::error::Error;
use nym_crypto::asymmetric::identity;
use schemars::JsonSchema;
@@ -61,7 +61,16 @@ impl SignedHostInformation {
return false;
};
self.verify(&pub_key)
if self.verify(&pub_key) {
return true;
}
// attempt to verify legacy signature
SignedData {
data: LegacyHostInformation::from(self.data.clone()),
signature: self.signature.clone(),
}
.verify(&pub_key)
}
}
@@ -84,3 +93,71 @@ impl Display for ErrorResponse {
self.message.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use nym_crypto::asymmetric::{ed25519, x25519};
use rand_chacha::rand_core::SeedableRng;
#[test]
fn dummy_signed_host_verification() {
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
let ed22519 = ed25519::KeyPair::new(&mut rng);
let x25519_sphinx = x25519::KeyPair::new(&mut rng);
let host_info = crate::api::v1::node::models::HostInformation {
ip_address: vec!["1.1.1.1".parse().unwrap()],
hostname: Some("foomp.com".to_string()),
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: ed22519.public_key().to_base58_string(),
x25519_sphinx: x25519_sphinx.public_key().to_base58_string(),
x25519_noise: "".to_string(),
},
};
let signed_info = SignedHostInformation::new(host_info, ed22519.private_key()).unwrap();
assert!(signed_info.verify(ed22519.public_key()));
assert!(signed_info.verify_host_information())
}
#[test]
fn dummy_legacy_signed_host_verification() {
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
let ed22519 = ed25519::KeyPair::new(&mut rng);
let x25519_sphinx = x25519::KeyPair::new(&mut rng);
let legacy_info = crate::api::v1::node::models::LegacyHostInformation {
ip_address: vec!["1.1.1.1".parse().unwrap()],
hostname: Some("foomp.com".to_string()),
keys: crate::api::v1::node::models::LegacyHostKeys {
ed25519: ed22519.public_key().to_base58_string(),
x25519: x25519_sphinx.public_key().to_base58_string(),
},
};
let host_info = crate::api::v1::node::models::HostInformation {
ip_address: legacy_info.ip_address.clone(),
hostname: legacy_info.hostname.clone(),
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: legacy_info.keys.ed25519.clone(),
x25519_sphinx: legacy_info.keys.x25519.clone(),
x25519_noise: "".to_string(),
},
};
// signature on legacy data
let signature = SignedData::new(legacy_info, ed22519.private_key())
.unwrap()
.signature;
// signed blob with the 'current' structure
let current_struct = SignedData {
data: host_info,
signature,
};
assert!(!current_struct.verify(ed22519.public_key()));
assert!(current_struct.verify_host_information())
}
}
@@ -7,6 +7,9 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Gateway {
#[serde(default)]
pub enforces_zk_nyms: bool,
pub client_interfaces: ClientInterfaces,
}
@@ -32,6 +32,23 @@ pub struct HostInformation {
pub keys: HostKeys,
}
#[derive(Serialize)]
pub struct LegacyHostInformation {
pub ip_address: Vec<IpAddr>,
pub hostname: Option<String>,
pub keys: LegacyHostKeys,
}
impl From<HostInformation> for LegacyHostInformation {
fn from(value: HostInformation) -> Self {
LegacyHostInformation {
ip_address: value.ip_address,
hostname: value.hostname,
keys: value.keys.into(),
}
}
}
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct HostKeys {
@@ -49,6 +66,21 @@ pub struct HostKeys {
pub x25519_noise: String,
}
impl From<HostKeys> for LegacyHostKeys {
fn from(value: HostKeys) -> Self {
LegacyHostKeys {
ed25519: value.ed25519_identity,
x25519: value.x25519_sphinx,
}
}
}
#[derive(Serialize)]
pub struct LegacyHostKeys {
pub ed25519: String,
pub x25519: String,
}
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct HostSystem {
+30 -3
View File
@@ -5,8 +5,12 @@ use crate::cli::helpers::{
EntryGatewayArgs, ExitGatewayArgs, HostArgs, HttpArgs, MixnetArgs, MixnodeArgs, WireguardArgs,
};
use crate::node::description::save_node_description;
use crate::node::helpers::{load_ed25519_identity_public_key, store_x25519_noise_keypair};
use crate::node::helpers::{
bonding_version, load_ed25519_identity_public_key, store_x25519_noise_keypair,
};
use clap::ValueEnum;
use colored::Color::TrueColor;
use colored::Colorize;
use nym_crypto::asymmetric::x25519;
use nym_gateway::helpers::{load_ip_packet_router_config, load_network_requester_config};
use nym_gateway::GatewayError;
@@ -223,6 +227,7 @@ async fn migrate_mixnode(mut args: Args) -> Result<(), NymNodeError> {
packet_forwarding_maximum_backoff: cfg.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: cfg.debug.initial_connection_timeout,
maximum_connection_buffer_size: cfg.debug.maximum_connection_buffer_size,
..Default::default()
},
..Default::default()
}))
@@ -385,6 +390,7 @@ async fn migrate_gateway(mut args: Args) -> Result<(), NymNodeError> {
packet_forwarding_maximum_backoff: cfg.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: cfg.debug.initial_connection_timeout,
maximum_connection_buffer_size: cfg.debug.maximum_connection_buffer_size,
..Default::default()
},
}))
.with_mixnode(args.mixnode.override_config_section(config::MixnodeConfig {
@@ -625,7 +631,28 @@ pub(crate) async fn execute(args: Args) -> Result<(), NymNodeError> {
trace!("args: {args:#?}");
match args.node_type {
NodeType::Mixnode => migrate_mixnode(args).await,
NodeType::Gateway => migrate_gateway(args).await,
NodeType::Mixnode => migrate_mixnode(args).await?,
NodeType::Gateway => migrate_gateway(args).await?,
}
let orange = TrueColor {
r: 251,
g: 110,
b: 78,
};
println!("{}", "** Attention **".color(orange).bold());
print!("Please consider updating the '");
print!("{}", "version".color(orange));
print!("' field of your ");
print!("{}", "existing".bold().underline());
println!(" node to:");
println!();
println!("{}", bonding_version().bold().color(orange));
println!();
print!("in the settings section of the ");
println!("{}", "Nym Wallet".bold().color(orange));
println!();
Ok(())
}
+6 -5
View File
@@ -18,6 +18,7 @@ pub(crate) async fn execute(mut args: Args) -> Result<(), NymNodeError> {
let config_path = args.config.config_path();
let output = args.output;
let bonding_info_path = args.bonding_information_output.clone();
let init_only = args.init_only;
let config = if !config_path.exists() {
debug!("no configuration file found at '{}'", config_path.display());
@@ -25,16 +26,11 @@ pub(crate) async fn execute(mut args: Args) -> Result<(), NymNodeError> {
if args.deny_init {
return Err(NymNodeError::ForbiddenInitialisation { config_path });
}
let init_only = args.init_only;
let maybe_custom_mnemonic = args.take_mnemonic();
let config = args.build_config()?;
NymNode::initialise(&config, maybe_custom_mnemonic).await?;
if init_only {
debug!("returning due to the 'init-only' flag");
return Ok(());
}
config
} else {
@@ -73,5 +69,10 @@ pub(crate) async fn execute(mut args: Args) -> Result<(), NymNodeError> {
})?;
}
if init_only {
debug!("returning due to the 'init-only' flag");
return Ok(());
}
nym_node.run().await
}
+22
View File
@@ -195,6 +195,22 @@ pub(crate) struct MixnetArgs {
env = NYMNODE_NYM_APIS_ARG
)]
pub(crate) nym_api_urls: Option<Vec<Url>>,
/// Addresses to nyxd chain endpoint which the node will use for chain interactions.
#[clap(
long,
value_delimiter = ',',
env = NYMNODE_NYXD_URLS_ARG
)]
pub(crate) nyxd_urls: Option<Vec<Url>>,
/// Specifies whether this node should **NOT** use noise protocol in the connections (currently not implemented)
#[clap(
hide = true,
long,
env = NYMNODE_UNSAFE_DISABLE_NOISE
)]
pub(crate) unsafe_disable_noise: bool,
}
impl MixnetArgs {
@@ -210,6 +226,12 @@ impl MixnetArgs {
if let Some(nym_api_urls) = self.nym_api_urls {
section.nym_api_urls = nym_api_urls
}
if let Some(nyxd_urls) = self.nyxd_urls {
section.nyxd_urls = nyxd_urls
}
if self.unsafe_disable_noise {
section.debug.unsafe_disable_noise = true
}
section
}
}
+5
View File
@@ -437,6 +437,9 @@ pub struct MixnetDebug {
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Specifies whether this node should **NOT** use noise protocol in the connections (currently not implemented)
pub unsafe_disable_noise: bool,
}
impl MixnetDebug {
@@ -453,6 +456,8 @@ impl Default for MixnetDebug {
packet_forwarding_maximum_backoff: Self::DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: Self::DEFAULT_INITIAL_CONNECTION_TIMEOUT,
maximum_connection_buffer_size: Self::DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
// to be changed by @SW once the implementation is there
unsafe_disable_noise: true,
}
}
}
+2
View File
@@ -34,6 +34,8 @@ pub mod vars {
// mixnet:
pub const NYMNODE_MIXNET_BIND_ADDRESS_ARG: &str = "NYMNODE_MIXNET_BIND_ADDRESS";
pub const NYMNODE_NYM_APIS_ARG: &str = "NYMNODE_NYM_APIS";
pub const NYMNODE_NYXD_URLS_ARG: &str = "NYMNODE_NYXD";
pub const NYMNODE_UNSAFE_DISABLE_NOISE: &str = "UNSAFE_DISABLE_NOISE";
// wireguard:
pub const NYMNODE_WG_ENABLED_ARG: &str = "NYMNODE_WG_ENABLED";
+5 -32
View File
@@ -1,10 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::helpers::{load_ed25519_identity_public_key, load_x25519_sphinx_public_key};
use crate::node::helpers::{
bonding_version, load_ed25519_identity_public_key, load_x25519_sphinx_public_key,
};
use nym_node::config::{Config, NodeMode};
use nym_node::error::NymNodeError;
use semver::{BuildMetadata, Version};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
@@ -60,26 +61,12 @@ impl MixnodeBondingInformation {
x25519_sphinx_key: String,
) -> MixnodeBondingInformation {
MixnodeBondingInformation {
version: Self::get_version(),
version: bonding_version(),
host: "YOU NEED TO FILL THIS FIELD MANUALLY".to_string(),
identity_key: ed25519_identity_key,
sphinx_key: x25519_sphinx_key,
}
}
#[allow(clippy::unwrap_used)]
fn get_version() -> String {
// SAFETY:
// 1. the value has been put into the environment during build.rs, so it must exist,
// 2. and the obtained version has already been parsed into semver in build.rs, so it must be a valid semver
let raw = include_str!(concat!(env!("OUT_DIR"), "/mixnode_version"));
let mut semver: Version = raw.parse().unwrap();
// if it's not empty, then we messed up our own versioning
assert!(semver.build.is_empty());
semver.build = BuildMetadata::new("nymnode").unwrap();
semver.to_string()
}
}
impl Display for MixnodeBondingInformation {
@@ -108,27 +95,13 @@ impl GatewayBondingInformation {
x25519_sphinx_key: String,
) -> GatewayBondingInformation {
GatewayBondingInformation {
version: Self::get_version(),
version: bonding_version(),
host: "YOU NEED TO FILL THIS FIELD MANUALLY".to_string(),
location: "YOU NEED TO FILL THIS FIELD MANUALLY".to_string(),
identity_key: ed25519_identity_key,
sphinx_key: x25519_sphinx_key,
}
}
#[allow(clippy::unwrap_used)]
fn get_version() -> String {
// SAFETY:
// 1. the value has been put into the file during build.rs, so it must exist,
// 2. and the obtained version has already been parsed into semver in build.rs, so it must be a valid semver
let raw = include_str!(concat!(env!("OUT_DIR"), "/gateway_version"));
let mut semver: Version = raw.parse().unwrap();
// if it's not empty, then we messed up our own versioning
assert!(semver.build.is_empty());
semver.build = BuildMetadata::new("nymnode").unwrap();
semver.to_string()
}
}
impl Display for GatewayBondingInformation {
+14
View File
@@ -7,10 +7,24 @@ use nym_node::error::{KeyIOFailure, NymNodeError};
use nym_node_http_api::api::api_requests::v1::node::models::NodeDescription;
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use nym_pemstore::KeyPairPath;
use semver::{BuildMetadata, Version};
use serde::Serialize;
use std::fmt::{Display, Formatter};
use std::path::Path;
#[allow(clippy::unwrap_used)]
pub fn bonding_version() -> String {
// SAFETY:
// the value has been put there by cargo
let raw = env!("CARGO_PKG_VERSION");
let mut semver: Version = raw.parse().unwrap();
// if it's not empty, then we messed up our own versioning
assert!(semver.build.is_empty());
semver.build = BuildMetadata::new("nymnode").unwrap();
semver.to_string()
}
#[derive(Debug, Serialize)]
pub(crate) struct DisplayDetails {
pub(crate) current_mode: NodeMode,
+7 -1
View File
@@ -16,13 +16,19 @@ pub(crate) fn sign_host_details(
x25519_noise: &x25519::PublicKey,
ed22519_identity: &ed25519::KeyPair,
) -> Result<api_requests::v1::node::models::SignedHostInformation, NymNodeError> {
let x25519_noise = if config.mixnet.debug.unsafe_disable_noise {
String::new()
} else {
x25519_noise.to_base58_string()
};
let host_info = api_requests::v1::node::models::HostInformation {
ip_address: config.host.public_ips.clone(),
hostname: config.host.hostname.clone(),
keys: api_requests::v1::node::models::HostKeys {
ed25519_identity: ed22519_identity.public_key().to_base58_string(),
x25519_sphinx: x22519_sphinx.to_base58_string(),
x25519_noise: x25519_noise.to_base58_string(),
x25519_noise,
},
};
+1
View File
@@ -484,6 +484,7 @@ impl NymNode {
wss_port: self.config.entry_gateway.announce_wss_port,
});
let gateway_details = api_requests::v1::gateway::models::Gateway {
enforces_zk_nyms: self.config.entry_gateway.enforce_zk_nyms,
client_interfaces: api_requests::v1::gateway::models::ClientInterfaces {
wireguard,
mixnet_websockets,
+1
View File
@@ -130,6 +130,7 @@ impl OutfoxPacket {
// We know that we'll always get 4 nodes, so we can unwrap here
let processing_node = nodes.last().unwrap();
let destination_node = nodes.first().unwrap();
OsRng.fill_bytes(&mut secret_key);
stage_params.encode_mix_layer(
&mut buffer[range],
&secret_key,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym_wallet"
version = "1.2.12"
version = "1.2.13"
description = "Nym Native Wallet"
authors = ["Nym Technologies SA"]
license = ""
+1 -1
View File
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-wallet",
"version": "1.2.12"
"version": "1.2.13"
},
"build": {
"distDir": "../dist",
+15 -14
View File
@@ -3,30 +3,31 @@ import os.path
import sys
def add_mixnode(base_network, base_dir, mix_id):
def add_mixnode(base_network, base_dir, mix_id, port_delta):
with open(os.path.join(base_dir, "mix" + str(mix_id) + ".json"), "r") as json_blob:
mix_data = json.load(json_blob)
base_network["mixnodes"][str(mix_id)][0]["identity_key"] = mix_data["identity_key"]
base_network["mixnodes"][str(mix_id)][0]["sphinx_key"] = mix_data["sphinx_key"]
base_network["mixnodes"][str(mix_id)][0]["mix_port"] = mix_data["mix_port"]
base_network["mixnodes"][str(mix_id)][0]["mix_port"] = 10000 + port_delta
base_network["mixnodes"][str(mix_id)][0]["version"] = mix_data["version"]
base_network["mixnodes"][str(mix_id)][0]["host"] = mix_data["bind_address"]
base_network["mixnodes"][str(mix_id)][0]["layer"] = mix_id
base_network["mixnodes"][str(mix_id)][0]["host"] = "127.0.0.1"
base_network["mixnodes"][str(mix_id)][0]["layer"] = mix_id % 3 + 1
base_network["mixnodes"][str(mix_id)][0]["mix_id"] = mix_id
base_network["mixnodes"][str(mix_id)][0]["owner"] = "whatever"
base_network["mixnodes"][str(mix_id)][0]["owner"] = "n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf"
return base_network
def add_gateway(base_network, base_dir):
def add_gateway(base_network, base_dir, port_delta):
with open(os.path.join(base_dir, "gateway.json"), "r") as json_blob:
gateway_data = json.load(json_blob)
base_network["gateways"][0]["identity_key"] = gateway_data["identity_key"]
base_network["gateways"][0]["sphinx_key"] = gateway_data["sphinx_key"]
base_network["gateways"][0]["mix_port"] = gateway_data["mix_port"]
base_network["gateways"][0]["clients_port"] = gateway_data["clients_port"]
base_network["gateways"][0]["mix_port"] = 10000 + port_delta
base_network["gateways"][0]["clients_port"] = 9000
# base_network["gateways"][0]["version"] = gateway_data["version"]
base_network["gateways"][0]["host"] = gateway_data["bind_address"]
base_network["gateways"][0]["owner"] = "whatever"
base_network["gateways"][0]["host"] = "127.0.0.1"
base_network["gateways"][0]["owner"] = "n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf"
return base_network
@@ -41,10 +42,10 @@ def main(args):
}
base_dir = args[0]
base_network = add_mixnode(base_network, base_dir, 1)
base_network = add_mixnode(base_network, base_dir, 2)
base_network = add_mixnode(base_network, base_dir, 3)
base_network = add_gateway(base_network, base_dir)
base_network = add_mixnode(base_network, base_dir, 1, 1)
base_network = add_mixnode(base_network, base_dir, 2, 2)
base_network = add_mixnode(base_network, base_dir, 3, 3)
base_network = add_gateway(base_network, base_dir, 4)
with open(os.path.join(base_dir, "network.json"), "w") as out:
json.dump(base_network, out, indent=2)
+8 -8
View File
@@ -20,16 +20,16 @@ echo "Using $localnetdir for the localnet"
# initialise mixnet
echo "initialising mixnode1..."
cargo run --release --bin nym-mixnode -- init --id "mix1-$suffix" --host 127.0.0.1 --mix-port 10001 --verloc-port 20001 --http-api-port 30001 --metrics-key=lala --output=json >>"$localnetdir/mix1.json"
cargo run --release --bin nym-node -- run --id "mix1-$suffix" --init-only --mixnet-bind-address=127.0.0.1:10001 --verloc-bind-address 127.0.0.1:20001 --http-bind-address 127.0.0.1:30001 --http-access-token=lala --output=json --bonding-information-output "$localnetdir/mix1.json"
echo "initialising mixnode2..."
cargo run --release --bin nym-mixnode -- init --id "mix2-$suffix" --host 127.0.0.1 --mix-port 10002 --verloc-port 20002 --http-api-port 30002 --metrics-key=lala --output=json >>"$localnetdir/mix2.json"
cargo run --release --bin nym-node -- run --id "mix2-$suffix" --init-only --mixnet-bind-address=127.0.0.1:10002 --verloc-bind-address 127.0.0.1:20002 --http-bind-address 127.0.0.1:30002 --http-access-token=lala --output=json --bonding-information-output "$localnetdir/mix2.json"
echo "initialising mixnode3..."
cargo run --release --bin nym-mixnode -- init --id "mix3-$suffix" --host 127.0.0.1 --mix-port 10003 --verloc-port 20003 --http-api-port 30003 --metrics-key=lala --output=json >>"$localnetdir/mix3.json"
cargo run --release --bin nym-node -- run --id "mix3-$suffix" --init-only --mixnet-bind-address=127.0.0.1:10003 --verloc-bind-address 127.0.0.1:20003 --http-bind-address 127.0.0.1:30003 --http-access-token=lala --output=json --bonding-information-output "$localnetdir/mix3.json"
echo "initialising gateway..."
cargo run --release --bin nym-gateway -- init --id "gateway-$suffix" --host 127.0.0.1 --mix-port 10004 --clients-port 9000 --output=json >>"$localnetdir/gateway.json"
cargo run --release --bin nym-node -- run --id "gateway-$suffix" --init-only --mode entry --mixnet-bind-address=127.0.0.1:10004 --entry-bind-address 127.0.0.1:9000 --verloc-bind-address 127.0.0.1:20004 --http-bind-address 127.0.0.1:30004 --http-access-token=lala --output=json --bonding-information-output "$localnetdir/gateway.json"
# build the topology
echo "combining json files..."
@@ -42,10 +42,10 @@ echo "the full network file is located at $networkfile"
echo "starting the mixnet..."
tmux start-server
tmux new-session -d -s localnet -n Mixnet -d "/usr/bin/env sh -c \" cargo run --release --bin nym-mixnode -- run --id mix1-$suffix \""
tmux split-window -t localnet:0 "/usr/bin/env sh -c \" cargo run --release --bin nym-mixnode -- run --id mix2-$suffix \""
tmux split-window -t localnet:0 "/usr/bin/env sh -c \" cargo run --release --bin nym-mixnode -- run --id mix3-$suffix \""
tmux split-window -t localnet:0 "/usr/bin/env sh -c \" cargo run --release --bin nym-gateway -- run --id gateway-$suffix --local \""
tmux new-session -d -s localnet -n Mixnet -d "/usr/bin/env sh -c \" cargo run --release --bin nym-node -- run --id mix1-$suffix \""
tmux split-window -t localnet:0 "/usr/bin/env sh -c \" cargo run --release --bin nym-node -- run --id mix2-$suffix \""
tmux split-window -t localnet:0 "/usr/bin/env sh -c \" cargo run --release --bin nym-node -- run --id mix3-$suffix \""
tmux split-window -t localnet:0 "/usr/bin/env sh -c \" cargo run --release --bin nym-node -- run --id gateway-$suffix \""
while ! nc -z localhost 9000; do
echo "waiting for nym-gateway to launch on port 9000..."
+4 -4
View File
@@ -1424,9 +1424,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "eyre"
version = "0.6.11"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
@@ -1841,9 +1841,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.24"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
"bytes",
"fnv",
+4 -4
View File
@@ -1424,9 +1424,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "eyre"
version = "0.6.11"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
@@ -1841,9 +1841,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.24"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
"bytes",
"fnv",
+2 -2
View File
@@ -1424,9 +1424,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "eyre"
version = "0.6.11"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
+1 -1
View File
@@ -39,7 +39,7 @@ mod socks5_client;
mod traits;
pub use client::{DisconnectedMixnetClient, IncludedSurbs, MixnetClientBuilder};
pub use config::{Config, KeyMode};
pub use config::Config;
pub use native_client::MixnetClient;
pub use native_client::MixnetClientSender;
pub use nym_client_core::{
+117 -113
View File
@@ -10,8 +10,11 @@ use crate::NymNetworkDetails;
use crate::{Error, Result};
use futures::channel::mpsc;
use futures::StreamExt;
use log::warn;
use nym_client_core::client::base_client::storage::helpers::get_all_registered_identities;
use log::{debug, warn};
use nym_client_core::client::base_client::storage::helpers::{
get_active_gateway_identity, get_all_registered_identities, has_gateway_details,
set_active_gateway,
};
use nym_client_core::client::base_client::storage::{
Ephemeral, GatewaysDetailsStore, MixnetClientStorage, OnDiskPersistent,
};
@@ -23,6 +26,7 @@ use nym_client_core::client::{
use nym_client_core::config::DebugConfig;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::setup_gateway;
use nym_client_core::init::types::{GatewaySelectionSpecification, GatewaySetup};
use nym_network_defaults::WG_TUN_DEVICE_IP_ADDRESS;
use nym_socks5_client_core::config::Socks5;
@@ -248,13 +252,15 @@ where
/// Construct a [`DisconnectedMixnetClient`] from the setup specified.
pub fn build(self) -> Result<DisconnectedMixnetClient<S>> {
let client = DisconnectedMixnetClient::new(self.config, self.socks5_config, self.storage)?
.custom_gateway_transceiver(self.custom_gateway_transceiver)
.custom_topology_provider(self.custom_topology_provider)
.custom_shutdown(self.custom_shutdown)
.wireguard_mode(self.wireguard_mode)
.wait_for_gateway(self.wait_for_gateway)
.force_tls(self.force_tls);
let mut client =
DisconnectedMixnetClient::new(self.config, self.socks5_config, self.storage)?;
client.custom_gateway_transceiver = self.custom_gateway_transceiver;
client.custom_topology_provider = self.custom_topology_provider;
client.custom_shutdown = self.custom_shutdown;
client.wireguard_mode = self.wireguard_mode;
client.wait_for_gateway = self.wait_for_gateway;
client.force_tls = self.force_tls;
Ok(client)
}
@@ -355,48 +361,6 @@ where
})
}
#[must_use]
pub fn custom_shutdown(mut self, shutdown: Option<TaskClient>) -> Self {
self.custom_shutdown = shutdown;
self
}
#[must_use]
pub fn custom_topology_provider(
mut self,
provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
) -> Self {
self.custom_topology_provider = provider;
self
}
#[must_use]
pub fn custom_gateway_transceiver(
mut self,
gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send + Sync>>,
) -> Self {
self.custom_gateway_transceiver = gateway_transceiver;
self
}
#[must_use]
pub fn wireguard_mode(mut self, wireguard_mode: bool) -> Self {
self.wireguard_mode = wireguard_mode;
self
}
#[must_use]
pub fn wait_for_gateway(mut self, wait_for_gateway: bool) -> Self {
self.wait_for_gateway = wait_for_gateway;
self
}
#[must_use]
pub fn force_tls(mut self, must_use_tls: bool) -> Self {
self.force_tls = must_use_tls;
self
}
fn get_api_endpoints(&self) -> Vec<Url> {
self.config
.network_details
@@ -426,6 +390,65 @@ where
}
}
async fn setup_client_keys(&self) -> Result<()> {
let mut rng = OsRng;
let key_store = self.storage.key_store();
if key_store.load_keys().await.is_err() {
debug!("Generating new client keys");
nym_client_core::init::generate_new_client_keys(&mut rng, key_store).await?;
}
Ok(())
}
async fn print_all_registered_gateway_identities(&self) {
match get_all_registered_identities(self.storage.gateway_details_store()).await {
Err(err) => {
warn!("failed to query for all registered gateways: {err}")
}
Ok(all_ids) => {
if !all_ids.is_empty() {
debug!("this client is already registered with the following gateways:");
for id in all_ids {
debug!("{id}")
}
}
}
}
}
async fn print_selected_gateway(&self) {
match self.storage.gateway_details_store().active_gateway().await {
Err(err) => {
warn!("failed to query for the current active gateway: {err}")
}
Ok(active) => {
if let Some(active) = active.registration {
let id = active.details.gateway_id();
debug!("currently selected gateway: {0}", id);
}
}
}
}
async fn set_active_gateway_if_previously_registered(
&self,
user_chosen_gateway: &str,
) -> Result<bool> {
let storage = self.storage.gateway_details_store();
// Stricly speaking, `set_active_gateway` does this check internally as well, but since the
// error is boxed away and we're using a generic storage, it's not so easy to match on it.
// This function is at least less likely to fail on something unrelated to the existence of
// the gateway in the set of registered gateways
if has_gateway_details(storage, user_chosen_gateway).await? {
set_active_gateway(storage, user_chosen_gateway).await?;
Ok(true)
} else {
Ok(false)
}
}
async fn new_gateway_setup(&self) -> Result<GatewaySetup, ClientCoreError> {
let nym_api_endpoints = self.get_api_endpoints();
@@ -445,67 +468,67 @@ where
})
}
/// Check if the client already has an active gateway enabled.
async fn has_active_gateway(&self) -> bool {
let storage = self.storage.gateway_details_store();
match storage.active_gateway().await {
Err(err) => {
warn!("failed to query for the current active gateway: {err}");
return false;
}
Ok(active) => {
if active.registration.is_some() {
return true;
}
}
}
match get_all_registered_identities(storage).await {
Err(err) => {
warn!("failed to query for all registered gateways: {err}")
}
Ok(all_ids) => {
if !all_ids.is_empty() {
warn!("this client doesn't have an active gateway set, however, it's already registered with the following gateways (consider making one of them active):");
for id in all_ids {
warn!("{id}")
}
}
}
}
false
}
/// Register with a gateway. If a gateway is provided in the config then that will try to be
/// used. If none is specified, a gateway at random will be picked.
/// used. If none is specified, a gateway at random will be picked. The used gateway is saved
/// as the active gateway.
///
/// # Errors
///
/// This function will return an error if you try to re-register when in an already registered
/// state.
pub async fn register_and_authenticate_gateway(&mut self) -> Result<()> {
pub async fn setup_gateway(&mut self) -> Result<()> {
if !matches!(self.state, BuilderState::New) {
return Err(Error::ReregisteringGatewayNotSupported);
}
log::debug!("Registering with gateway");
self.print_all_registered_gateway_identities().await;
self.print_selected_gateway().await;
let gateway_setup = if self.has_active_gateway().await {
GatewaySetup::MustLoad { gateway_id: None }
} else {
self.new_gateway_setup().await?
// Try to set active gateway to the same as the user chosen one, if it's in the set of
// gateways that is already registered.
if let Some(ref user_chosen_gateway) = self.config.user_chosen_gateway {
if self
.set_active_gateway_if_previously_registered(user_chosen_gateway)
.await?
{
debug!("user chosen gateway is already registered, set as active");
}
}
let active_gateway =
get_active_gateway_identity(self.storage.gateway_details_store()).await?;
// Determine the gateway setup based on the currently active gateway and the user-chosen
// gateway.
let gateway_setup = match (self.config.user_chosen_gateway.as_ref(), active_gateway) {
// When a user-chosen gateway exists and matches the active one.
(Some(user_chosen_gateway), Some(active_gateway))
if &active_gateway.to_base58_string() == user_chosen_gateway =>
{
GatewaySetup::MustLoad { gateway_id: None }
}
// When a user-chosen gateway exists but there's no active gateway, or it doesn't match the active one.
(Some(_), _) => self.new_gateway_setup().await?,
// When no user-chosen gateway exists but there's an active gateway.
(None, Some(_)) => GatewaySetup::MustLoad { gateway_id: None },
// When there's no user-chosen gateway and no active gateway.
(None, None) => self.new_gateway_setup().await?,
};
// this will perform necessary key and details load and optional store
let _init_result = nym_client_core::init::setup_gateway(
let init_results = setup_gateway(
gateway_setup,
self.storage.key_store(),
self.storage.gateway_details_store(),
)
.await?;
set_active_gateway(
self.storage.gateway_details_store(),
&init_results.gateway_id().to_base58_string(),
)
.await?;
self.state = BuilderState::Registered {};
Ok(())
}
@@ -527,13 +550,8 @@ where
}
async fn connect_to_mixnet_common(mut self) -> Result<(BaseClient, Recipient)> {
// if we don't care about our keys, explicitly register
if !self.config.key_mode.is_keep() {
self.register_and_authenticate_gateway().await?;
}
// otherwise, the whole key setup and gateway selection dance will be done for us
// when we start the base client
self.setup_client_keys().await?;
self.setup_gateway().await?;
let nyxd_endpoints = self.get_nyxd_endpoints();
let nym_api_endpoints = self.get_api_endpoints();
@@ -543,25 +561,11 @@ where
.config
.as_base_client_config(nyxd_endpoints, nym_api_endpoints.clone());
let known_gateway = self.has_active_gateway().await;
// if we have a known gateway, don't bother doing all of those queries
let gateway_setup = if known_gateway {
None
} else {
Some(self.new_gateway_setup().await?)
};
let mut base_builder: BaseClientBuilder<_, _> =
BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client)
.with_wait_for_gateway(self.wait_for_gateway)
.with_wireguard_connection(self.wireguard_mode);
if !known_gateway {
// safety: `gateway_setup` is always set whenever `known_gateway` is false
base_builder = base_builder.with_gateway_setup(gateway_setup.unwrap());
}
// let mut base_builder: BaseClientBuilder<_, _> = if !known_gateway {
// // we need to setup a new gateway
// let setup = self.new_gateway_setup().await;
-18
View File
@@ -5,30 +5,12 @@ use url::Url;
const DEFAULT_SDK_CLIENT_ID: &str = "_default-nym-sdk-client";
#[derive(Clone, Debug, Default)]
pub enum KeyMode {
/// Use existing key files if they exists, otherwise create new ones.
#[default]
Keep,
/// Create new keys, overwriting any potential previously existing keys.
Overwrite,
}
impl KeyMode {
pub(crate) fn is_keep(&self) -> bool {
matches!(self, KeyMode::Keep)
}
}
/// Config struct for [`crate::mixnet::MixnetClient`]
#[derive(Default)]
pub struct Config {
/// If the user has explicitly specified a gateway.
pub user_chosen_gateway: Option<String>,
/// Determines how to handle existing key files found.
pub key_mode: KeyMode,
/// The details of the network we're using. It defaults to the mainnet network.
pub network_details: NymNetworkDetails,

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