Compare commits

...

106 Commits

Author SHA1 Message Date
dainius-nym 7528109693 fix: update fx average rate calcs to ignore 0 values (#5454)
* fix: update fx average rate calcs to ignore 0 values

* chore: bump version and format the code
2025-02-13 09:50:32 +00:00
Jon Häggblad 203d682f2c Upgrade tower to 0.5.2 (#5446) 2025-02-13 10:43:39 +01:00
dependabot[bot] 589575eed8 build(deps): bump publicsuffix from 2.2.3 to 2.3.0 (#5367)
Bumps [publicsuffix](https://github.com/rushmorem/publicsuffix) from 2.2.3 to 2.3.0.
- [Release notes](https://github.com/rushmorem/publicsuffix/releases)
- [Commits](https://github.com/rushmorem/publicsuffix/compare/v2.2.3...v2.3.0)

---
updated-dependencies:
- dependency-name: publicsuffix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-13 00:50:34 +01:00
Jon Häggblad 35bf1cc717 Disable debug in wasm and wallet workflows too (#5459) 2025-02-13 00:16:32 +01:00
dependabot[bot] f5e02d5652 build(deps): bump hickory-proto from 0.24.2 to 0.24.3 (#5444)
* build(deps): bump hickory-proto from 0.24.2 to 0.24.3

Bumps [hickory-proto](https://github.com/hickory-dns/hickory-dns) from 0.24.2 to 0.24.3.
- [Release notes](https://github.com/hickory-dns/hickory-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.24.3/CHANGELOG.md)
- [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.2...v0.24.3)

---
updated-dependencies:
- dependency-name: hickory-proto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Don't downgrade rand_core

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2025-02-13 00:09:03 +01:00
dependabot[bot] 2fc641a7ff build(deps): bump hyper from 1.4.1 to 1.6.0 (#5416)
Bumps [hyper](https://github.com/hyperium/hyper) from 1.4.1 to 1.6.0.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v1.4.1...v1.6.0)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-13 00:05:39 +01:00
dependabot[bot] 0ccca19cc2 build(deps): bump uniffi_build from 0.25.3 to 0.29.0 (#5448)
* build(deps): bump uniffi_build from 0.25.3 to 0.29.0

Bumps [uniffi_build](https://github.com/mozilla/uniffi-rs) from 0.25.3 to 0.29.0.
- [Changelog](https://github.com/mozilla/uniffi-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mozilla/uniffi-rs/compare/v0.25.3...v0.29.0)

---
updated-dependencies:
- dependency-name: uniffi_build
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Also update uniffi to match uniffi_build

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2025-02-12 23:56:02 +01:00
Jon Häggblad a07e567eb2 Set debug to false in ci-build.yml (#5458) 2025-02-12 23:08:44 +01:00
Jon Häggblad f3400a0aa5 Add helper to extract a list of sqlite files with journal files wal/shm (#5452)
Co-authored-by: Andrej Mihajlov <andrej@nymtech.net>
2025-02-12 17:29:06 +01:00
dainius-nym bf8614a545 Feature/add gbp currency (#5453)
* features: add gbp currency to the fx price scrapper

* regenerated sqlx queries

* nump cargo version

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
2025-02-12 13:16:34 +00:00
dynco-nym b7e3687757 Dz nym node stats (#5418)
* Remove blacklisted, inactive, reserve fields

* Remove gw.blacklisted

* Remove blacklisted and bonded count

* DB operations

* Improve logging

* Remove unused functions

* get_nym_nodes for scraping WIP

* Separate nym_nodes from mixnode stats
- fixes FOREIGN_KEY_CONSTRAINT error when storing
  stats for nym_nodes which aren't in mixnodes table

* Daily aggregation works

* mixnodes/stats exposes correct info

* Undo unnecessary tidbits

* Replace obsolete stats

* Add total_stake

* Bump cargo.toml version

* Rename MixingNodeKind for better clarity
2025-02-11 12:07:15 +01:00
windy-ux b9b969b7d3 + specify worker-src (#5443)
+ CSP from main website

Co-authored-by: benedetta davico <46782255+benedettadavico@users.noreply.github.com>
2025-02-11 10:19:12 +00:00
dependabot[bot] 47303e5b3b build(deps): bump openssl from 0.10.56 to 0.10.70 in /nym-wallet (#5422)
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.56 to 0.10.70.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.56...openssl-v0.10.70)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 22:27:42 +01:00
dependabot[bot] 6b38ffd4f3 build(deps): bump hickory-proto from 0.24.2 to 0.24.3 in /nym-wallet (#5445)
Bumps [hickory-proto](https://github.com/hickory-dns/hickory-dns) from 0.24.2 to 0.24.3.
- [Release notes](https://github.com/hickory-dns/hickory-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.24.3/CHANGELOG.md)
- [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.2...v0.24.3)

---
updated-dependencies:
- dependency-name: hickory-proto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 22:19:43 +01:00
import this 169c313404 [DOCs/operators]: Email templates update (#5441)
* new intro template

* Update dmca_response.md
2025-02-10 19:11:03 +00:00
dependabot[bot] ccf430ea62 build(deps): bump the patch-updates group across 1 directory with 10 updates (#5439)
Bumps the patch-updates group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [async-trait](https://github.com/dtolnay/async-trait) | `0.1.85` | `0.1.86` |
| [clap](https://github.com/clap-rs/clap) | `4.5.27` | `4.5.28` |
| [comfy-table](https://github.com/nukesor/comfy-table) | `7.1.3` | `7.1.4` |
| [hickory-resolver](https://github.com/hickory-dns/hickory-dns) | `0.24.2` | `0.24.3` |
| [once_cell](https://github.com/matklad/once_cell) | `1.20.2` | `1.20.3` |
| [pin-project](https://github.com/taiki-e/pin-project) | `1.1.8` | `1.1.9` |
| [serde_json_path](https://github.com/hiltontj/serde_json_path) | `0.7.1` | `0.7.2` |
| [toml](https://github.com/toml-rs/toml) | `0.8.19` | `0.8.20` |
| [cosmrs](https://github.com/cosmos/cosmos-rust) | `0.21.0` | `0.21.1` |
| [tokio-postgres](https://github.com/sfackler/rust-postgres) | `0.7.12` | `0.7.13` |



Updates `async-trait` from 0.1.85 to 0.1.86
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.85...0.1.86)

Updates `clap` from 4.5.27 to 4.5.28
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.27...clap_complete-v4.5.28)

Updates `comfy-table` from 7.1.3 to 7.1.4
- [Release notes](https://github.com/nukesor/comfy-table/releases)
- [Changelog](https://github.com/Nukesor/comfy-table/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nukesor/comfy-table/compare/v7.1.3...v7.1.4)

Updates `hickory-resolver` from 0.24.2 to 0.24.3
- [Release notes](https://github.com/hickory-dns/hickory-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.24.3/CHANGELOG.md)
- [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.2...v0.24.3)

Updates `once_cell` from 1.20.2 to 1.20.3
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.2...v1.20.3)

Updates `pin-project` from 1.1.8 to 1.1.9
- [Release notes](https://github.com/taiki-e/pin-project/releases)
- [Changelog](https://github.com/taiki-e/pin-project/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/pin-project/compare/v1.1.8...v1.1.9)

Updates `serde_json_path` from 0.7.1 to 0.7.2
- [Release notes](https://github.com/hiltontj/serde_json_path/releases)
- [Changelog](https://github.com/hiltontj/serde_json_path/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hiltontj/serde_json_path/compare/v0.7.1...v0.7.2)

Updates `toml` from 0.8.19 to 0.8.20
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.19...toml-v0.8.20)

Updates `cosmrs` from 0.21.0 to 0.21.1
- [Commits](https://github.com/cosmos/cosmos-rust/compare/cosmrs/v0.21.0...cosmrs/v0.21.1)

Updates `tokio-postgres` from 0.7.12 to 0.7.13
- [Release notes](https://github.com/sfackler/rust-postgres/releases)
- [Commits](https://github.com/sfackler/rust-postgres/compare/tokio-postgres-v0.7.12...tokio-postgres-v0.7.13)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: comfy-table
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: hickory-resolver
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: pin-project
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: serde_json_path
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: cosmrs
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: tokio-postgres
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 14:59:07 +01:00
import this cf13b79e93 [DOCs/operators]: Clarify SGPv2 program rules (#5434) 2025-02-07 11:31:34 +00:00
Jon Häggblad 134a0196f8 Disable the test for checking the remaining bandwidth in nym-node-status-api (#5425)
* Disable the test for checking the remaining bandwidth in nym-node-status-api

This check fails almost every time on CI, possibly due to rate limiting?
It's not good to disable the check, but it's blocking CI as it stands
now. Given that we have the check above for locating the ip, we at least
have a little coverage.

* Remove unused
2025-02-07 11:39:37 +01:00
benedetta davico 6c45c9f0b0 Merge pull request #5396 from nymtech/fix/wallet-explorer-url
Change Explorer URL to new smooshed nodes
2025-02-06 16:47:26 +01:00
import this b5afae0916 [DOCs:operators]: Update nym-node specs (#5433)
* Update nym-node-specs.mdx

* update specs - PR finished
2025-02-06 15:43:33 +00:00
benedetta davico 988eca857f Merge pull request #5431 from nymtech/drazen/forget-cli-client
Push down forget me to client configs
2025-02-06 15:25:04 +01:00
benedetta davico 3c05db2874 Merge pull request #5428 from nymtech/release/2025.2-hu
Merge release/2025.2-hu to develop
2025-02-06 13:58:47 +01:00
durch a8e268f84a Push down forget me to client configs 2025-02-06 13:15:58 +01:00
benedetta davico ac22533ecd Merge pull request #5429 from nymtech/feature/fix_develop_merge
Feature/fix develop merge
2025-02-06 13:12:31 +01:00
Bogdan-Ștefan Neacşu bdc0b875a4 Merge remote-tracking branch 'origin/develop' into release/2025.2-hu 2025-02-06 13:16:51 +02:00
import this d7b67c1408 [DOCs]: hotfix relative path url (#5427) 2025-02-06 10:15:45 +00:00
import this 606e29ebb0 [DOCs/operators]: Release notes, new specs, legal pages (#5419)
* add legal support notes

* write dev release notes

* create new legal page and add templates

* remove node_api_check to backup

* templates page

* update specs

* update backup and restore node

* PR ready for review

* address review comment

* last tweaks - PR finished

* last tweaks - PR finished
2025-02-05 15:19:56 +00:00
Bogdan-Ștefan Neacşu 21e3c1538d Fix statistics shutdown (#5426) 2025-02-05 16:06:46 +02:00
mfahampshire 0fc7cc657d Max/openapi docs update (#5292)
* spacing + working openapi local for nymapi

* sandbox nyx rest api

* add now working nym-api openapi json url to component
2025-02-05 14:05:44 +00:00
dependabot[bot] 23a7f01c05 build(deps): bump tokio from 1.40.0 to 1.43.0 (#5370)
* build(deps): bump tokio from 1.40.0 to 1.43.0

Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.40.0 to 1.43.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.40.0...tokio-1.43.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* wip: test if token is set

* Try with an artifical delay between calls

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2025-02-05 10:38:28 +01:00
Jon Häggblad 3a21cfa1ab Make wait_for_graceful_shutdown to be pub (#5424) 2025-02-05 08:58:25 +01:00
Jack Wampler 1d2e6d916c Use secure DNS for websocket connection establishment (#5386)
implementation of secure dns for websocket connection establishment. depends on #5355
2025-02-04 11:20:39 -07:00
benedettadavico 4c2bf3642e update changelong 2025-02-04 10:29:48 +01:00
Jędrzej Stuczyński 70e2e32385 Feature/remove double spending bloomfilter (#5417)
* removed all uses of the bloomfilter inside nym-api

* changed http status code on bf queries
2025-02-03 16:11:13 +00:00
Jon Häggblad 68a192daa3 Upgrade to thiserror 2.0 (#5414)
* Upgrade to thiserror 2.0

* Remove line macros in vesting contract error type

* Name positional arguments in GatewayRequestsError

* Named positional argument

* Revert "Remove line macros in vesting contract error type"

This reverts commit 49f937da3f.

* Use positional arguments for line
2025-02-03 10:50:11 +01:00
dependabot[bot] d6aacae14e build(deps): bump the patch-updates group across 1 directory with 9 updates (#5406)
Bumps the patch-updates group with 9 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [clap](https://github.com/clap-rs/clap) | `4.5.26` | `4.5.27` |
| [clap_complete](https://github.com/clap-rs/clap) | `4.5.40` | `4.5.44` |
| [getset](https://github.com/jbaublitz/getset) | `0.1.3` | `0.1.4` |
| [indicatif](https://github.com/console-rs/indicatif) | `0.17.9` | `0.17.11` |
| [log](https://github.com/rust-lang/log) | `0.4.22` | `0.4.25` |
| [pin-project](https://github.com/taiki-e/pin-project) | `1.1.7` | `1.1.8` |
| [semver](https://github.com/dtolnay/semver) | `1.0.24` | `1.0.25` |
| [serde_json](https://github.com/serde-rs/json) | `1.0.135` | `1.0.138` |
| [bip32](https://github.com/iqlusioninc/crates) | `0.5.2` | `0.5.3` |



Updates `clap` from 4.5.26 to 4.5.27
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.26...clap_complete-v4.5.27)

Updates `clap_complete` from 4.5.40 to 4.5.44
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.40...clap_complete-v4.5.44)

Updates `getset` from 0.1.3 to 0.1.4
- [Release notes](https://github.com/jbaublitz/getset/releases)
- [Commits](https://github.com/jbaublitz/getset/compare/0.1.3...0.1.4)

Updates `indicatif` from 0.17.9 to 0.17.11
- [Release notes](https://github.com/console-rs/indicatif/releases)
- [Commits](https://github.com/console-rs/indicatif/compare/0.17.9...0.17.11)

Updates `log` from 0.4.22 to 0.4.25
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.22...0.4.25)

Updates `pin-project` from 1.1.7 to 1.1.8
- [Release notes](https://github.com/taiki-e/pin-project/releases)
- [Changelog](https://github.com/taiki-e/pin-project/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/pin-project/compare/v1.1.7...v1.1.8)

Updates `semver` from 1.0.24 to 1.0.25
- [Release notes](https://github.com/dtolnay/semver/releases)
- [Commits](https://github.com/dtolnay/semver/compare/1.0.24...1.0.25)

Updates `serde_json` from 1.0.135 to 1.0.138
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.135...v1.0.138)

Updates `bip32` from 0.5.2 to 0.5.3
- [Commits](https://github.com/iqlusioninc/crates/compare/bip32/v0.5.2...bip32/v0.5.3)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: getset
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: indicatif
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: pin-project
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: semver
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: bip32
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-31 11:27:52 +01:00
Jon Häggblad 6f00023d09 Send shutdown instead of panic when reaching max fail (#5398)
* Send shutdown instead of panic when reaching max fail

* Stop quicker on failure

* Update comment
2025-01-31 10:39:37 +01:00
Tommy Verrall 982ec56874 Merge pull request #5300 from nymtech/feat/nymnode-entrypoint-docker
Nymnode entrypoint docker
2025-01-31 09:08:50 +01:00
Jack Wampler 5dcc1ed6dc Merge pull request #5401 from nymtech/jmwample/nym-api-route
Relocate a validator api function
2025-01-30 09:50:58 -07:00
Jon Häggblad d62bc0a10b Downgrade harmless log message from info to debug (#5403) 2025-01-30 13:36:06 +01:00
benedetta davico d1fb926a2a Merge pull request #5405 from nymtech/downgrade-to-debug
HU - Downgrade harmless log message from info to debug
2025-01-30 11:34:14 +01:00
benedettadavico dea69acd49 Downgrade harmless log message from info to debug 2025-01-30 11:32:54 +01:00
Tommy Verrall ada2d2247a Merge pull request #5404 from nymtech/jstuczyn-patch-1
lower default ticket verification quorum to 0.7
2025-01-30 11:28:32 +01:00
Jędrzej Stuczyński 0159d7c27a lower default ticket verification quorum to 0.7 2025-01-30 10:16:41 +00:00
jmwample 882003c08c fmt 2025-01-29 14:58:04 -07:00
jmwample b71a491872 relocate a validator api function 2025-01-29 14:55:16 -07:00
Yana Matrosova 8f48ae08c4 Redirect from mixnode page to nodes page (#5397)
Co-authored-by: Yana <yanok87@users.noreply.github.com>
2025-01-28 17:30:45 +00:00
Yana 31b9623407 Change Explorer URL to new smooshed nodes 2025-01-28 13:00:01 +02:00
Jędrzej Stuczyński 6d90ffdd2c reduce log severity for checking topology validity (#5395) 2025-01-28 09:29:51 +00:00
benedettadavico 28997c7f97 adding changelog for hu 2025-01-28 09:02:54 +01:00
Drazen Urch 9550934d1f Pre shutdown hooks for GatewayClient (#5381) 2025-01-27 20:00:37 +01:00
Jędrzej Stuczyński a6c586a33b chore :update version of chain watcher and validator rewarder (#5394) 2025-01-27 15:47:37 +00:00
Jędrzej Stuczyński 7c85c1a271 bugfix: correctly handle ingore epoch roles flag (#5390) 2025-01-24 15:35:06 +00:00
Jędrzej Stuczyński 92c8d1b73f bugfix: terminate mixnet socket listener on shutdown (#5389) 2025-01-24 12:59:14 +00:00
Jędrzej Stuczyński 554e9ca490 feat: make client ignore dual mode nodes by default (#5388) 2025-01-24 12:07:25 +00:00
import this ff91d4619e [HOTFIX/DOCs]: Update pre-built-binaries.mdx (#5385) 2025-01-24 10:31:19 +00:00
Jack Wampler 9d01474277 Merge pull request #5355 from nymtech/jmwample/dot
DNS resolver configuration for internal HTTP client lookups
2025-01-23 10:41:39 -07:00
jmwample 8d10552d7c hickory dns error mgmt 2025-01-23 08:29:56 -07:00
import this 04fd197f5a [DOCs]: Add more backup guides, clean up deprecated, fix URLs, add sha verf (#5384)
* fix socks5 syntax

* reshape backup and restore and add proxy

* fix URLS

* remove deprecated node-api-check - archived for when there is time to maintain the tool

* add hash verification step
2025-01-23 15:14:31 +00:00
Jon Häggblad 4eadaf8292 Fix missing path triggers for CI (#5380)
* Fix missing path triggers for CI

* Sort alphabetically to make it easier to maintain
2025-01-22 23:46:07 +01:00
jmwample 32e39ebc6b square cargo.lock with upstream branch 2025-01-22 14:32:04 -07:00
jmwample 117eb83a0b managing returned iterators 2025-01-22 14:30:16 -07:00
jmwample c964c137f4 fmt 2025-01-22 14:30:16 -07:00
jmwample 35b43d5b20 missed Lookup strategy 2025-01-22 14:30:16 -07:00
jmwample bf88b34898 fix wasm compile (exclude wasm target from DoH / DoT) 2025-01-22 14:30:16 -07:00
jmwample 93140a1aa7 minor fixes for clarity, interface access, and wasm exclusion 2025-01-22 14:30:16 -07:00
jmwample f594bfc9ab remove h3 because it causes an error 2025-01-22 14:30:12 -07:00
jmwample 4327e2945a DNS-over-X for internal domain name (i.e. API client) lookups 2025-01-22 14:29:44 -07:00
Bogdan-Ștefan Neacşu 6e6675f7bf Handle ecash network errors differently (#5378) 2025-01-22 15:46:05 +01:00
Bogdan-Ștefan Neacşu 8670693952 Uncouple storage reference for bandwidth client (#5372) 2025-01-22 12:12:06 +01:00
Bogdan-Ștefan Neacşu a7f7ebfbae Remove empty ephemeral keys (#5376) 2025-01-22 12:11:01 +01:00
mfahampshire 57c38ef222 temp remove cargodoc command (#5375) 2025-01-22 10:09:47 +00:00
Jędrzej Stuczyński 1aec8be85e fixed sql migration for adding default message timestamp (#5374) 2025-01-21 10:00:11 +00:00
benedettadavico 4b474dd8ff bump versions for hu 2025-01-20 15:34:23 +01:00
mfahampshire 8e05386a0b Max/tssdk docs maintenance (#5364)
* add temp warning
2025-01-20 13:02:56 +00:00
Tommy Verrall 13cfa55e6c Merge pull request #5327 from nymtech/marcdbz-patch-1
Update README.md
2025-01-20 09:36:25 +01:00
Tommy Verrall 18e628acde Merge pull request #5328 from nymtech/marcdbz-patch-2
Update README.md
2025-01-20 09:35:58 +01:00
Tommy Verrall b163dba2d4 Merge pull request #5356 from nymtech/release/2025.1-reeses
2025.1-reeses to master
2025-01-20 09:35:09 +01:00
import this e67b2b020a [DOCs/operators]: Bump release version (#5362)
* bump release version

* bump version in setup guide

* PR finished
2025-01-17 18:12:12 +00:00
benedetta davico 9b627dd70f Merge pull request #5363 from nymtech/fix-ci 2025-01-17 11:35:04 +01:00
Bogdan-Ștefan Neacşu 9a0b769425 Bind to [::] on nym-node for both IP versions (#5361)
* Bind to [::] on nym-node for both IP versions

* Force update to be run

* Fix after merging develop
2025-01-17 11:32:33 +01:00
Sachin Kamath 8e14f5f884 Update ci-build-upload-binaries.yml
remove observatory
2025-01-17 15:11:53 +05:30
import this 1b64cb42b0 [DOCs/operators]: Guides, changes and release-notes for v2025.1-reeses (#5340)
* create ToC snippet

* fund node client account

* revamp node guide

* finish setup page revamp

* add new update to changelog

* fix wallet dowload uls

* fix operator steps urls

* fix operator steps urls

* fix operator steps urls

* finish release notes

* finish changelog

* debug build

* correct links syntax

* add remote mnemonic pull command
2025-01-16 15:23:58 +00:00
Jędrzej Stuczyński 03c4895f2b feature: introduce /load endpoint for self-reported quantised NymNode load (#5326)
* feature: introduce /load endpoint for self-reported quantised NymNode load

* return Load::Unknown for value of 0 because it means we misread some data

* add additional filtering on 'en...' endpoints
2025-01-16 15:13:08 +00:00
Jędrzej Stuczyński dcfb092758 updated cosmrs and tendermint-rpc to their most recent versions (#5339) 2025-01-16 14:52:36 +00:00
Jędrzej Stuczyński 9305ad5364 exposed NymApiClient method for obtaining node performance history (#5360)
* exposed NymApiClient method for obtaining node performance history

* using path constants for route definition
2025-01-16 14:50:09 +00:00
Jędrzej Stuczyński ea5aef6c2f Client gateway selection (#5358)
* filter out dual-role gateways during selection

* changed behaviour of egress node validitiy
2025-01-16 14:24:27 +00:00
Jędrzej Stuczyński 61a4433cd9 chore: update indexed_db_futures (#5347)
* chore: update indexed_db_futures

* clippy
2025-01-16 14:23:43 +00:00
Fran Arbanas a0daabab03 fix version 2025-01-16 10:10:16 +01:00
Fran Arbanas b0a5b60945 update version 2025-01-16 10:06:34 +01:00
Marc 01c7ea72dd Update README.md
Fixed typo and updated operators link
2025-01-09 20:28:18 +01:00
Marc dfd1df5706 Update README.md
Updated the Tauri link
2025-01-09 20:26:04 +01:00
benedetta davico aa83501ed0 Merge pull request #5289 from nymtech/release/2024.14-crunch-patched
Merging patched crunch to master
2025-01-08 10:33:03 +01:00
Fran Arbanas 5f06414a12 bump version 2024-12-20 14:34:34 +01:00
Fran Arbanas 656838811a fix permissions 2024-12-20 14:34:10 +01:00
Fran Arbanas 7b8458630a bump version 2024-12-20 14:22:07 +01:00
Fran Arbanas cf2ab08b4d fix dockerfile 2024-12-20 14:20:43 +01:00
Fran Arbanas 2466112829 test version 2024-12-20 13:19:18 +01:00
Fran Arbanas e5306908e4 feat: add entrypoint script 2024-12-20 13:18:52 +01:00
benedetta davico b628a5f814 Merge pull request #5263 from nymtech/release/2024.14-crunch
Merge release/2024.14-crunch to master
2024-12-13 11:49:27 +01:00
benedetta davico 62045d76b3 Merge pull request #5172 from nymtech/release/2024.13-magura-patched
Update master with latest releases
2024-11-26 11:53:05 +01:00
benedetta davico f8317f5a03 Merge pull request #5025 from nymtech/release/2024.12-aero
Aero to master
2024-10-24 10:54:37 +02:00
benedetta davico c3ec970a37 Merge pull request #4928 from nymtech/release/2024.11-wedel
Release/2024.11-wedel to master
2024-09-26 08:24:53 +02:00
Jędrzej Stuczyński 5a573bc278 Merge pull request #4866 from nymtech/release/2024.10-caramello
Release/2024.10 caramello
2024-09-11 15:09:50 +01:00
benedetta davico 3d200db722 Merge pull request #4749 from nymtech/release/2024.9-topdeck-pre-develop-merge
release/2024.9 topdeck pre develop merge
2024-08-06 17:14:17 +02:00
Tommy Verrall e4139713cb Merge pull request #4724 from nymtech/release/2024.8-wispa
Merge release/2024.8-wispa into master
2024-07-24 08:25:52 +01:00
243 changed files with 8259 additions and 3100 deletions
@@ -79,7 +79,6 @@ jobs:
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-data-observatory
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
@@ -97,7 +96,6 @@ jobs:
cp target/release/nym-socks5-client $OUTPUT_DIR
cp target/release/nym-api $OUTPUT_DIR
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-data-observatory $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-node $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
+18 -3
View File
@@ -8,17 +8,18 @@ on:
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'nym-api/**'
- 'nym-data-observatory/**'
- 'nym-credential-proxy/**'
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
- 'sdk/lib/**'
- 'nyx-chain-watcher/**'
- 'sdk/ffi/**'
- 'sdk/rust/**'
- 'service-providers/**'
- 'nym-browser-extension/storage/**'
- 'tools/**'
- 'wasm/**'
- 'Cargo.toml'
@@ -53,6 +54,20 @@ jobs:
override: true
components: rustfmt, clippy
# To avoid running out of disk space, skip generating debug symbols
- name: Set debug to false (unix)
if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'mac')
run: |
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
git diff
- name: Set debug to false (win)
if: contains(matrix.os, 'windows')
shell: pwsh
run: |
(Get-Content Cargo.toml) -replace '\[profile.dev\]', "`$&`ndebug = false" | Set-Content Cargo.toml
git diff
- name: Check formatting
uses: actions-rs/cargo@v1
with:
+6
View File
@@ -30,6 +30,12 @@ jobs:
override: true
components: rustfmt, clippy
- name: Set debug to false
working-directory: nym-wallet
run: |
sed -i.bak '1s/^/\[profile.dev\]\ndebug = false\n\n/' Cargo.toml
git diff
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
+6
View File
@@ -1,6 +1,7 @@
name: ci-sdk-wasm
on:
workflow_dispatch:
pull_request:
paths:
- 'wasm/**'
@@ -44,6 +45,11 @@ jobs:
- name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli
- name: Set debug to false
run: |
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
git diff
- name: "Build"
run: make sdk-wasm-build
+80
View File
@@ -4,6 +4,86 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.2-hu] (2025-02-04)
- Feature/remove double spending bloomfilter ([#5417])
- HU - Downgrade harmless log message from info to debug ([#5405])
- lower default ticket verification quorum to 0.7 ([#5404])
- Downgrade harmless log message from info to debug ([#5403])
- Redirect from mixnode page to nodes page ([#5397])
- chore :update version of chain watcher and validator rewarder ([#5394])
- bugfix: correctly handle ingore epoch roles flag ([#5390])
- bugfix: terminate mixnet socket listener on shutdown ([#5389])
- feat: make client ignore dual mode nodes by default ([#5388])
- Handle ecash network errors differently ([#5378])
- Remove empty ephemeral keys ([#5376])
- fixed sql migration for adding default message timestamp ([#5374])
- Bind to [::] on nym-node for both IP versions ([#5361])
- exposed NymApiClient method for obtaining node performance history ([#5360])
- Client gateway selection ([#5358])
- chore: refresh wasm sdk ([#5353])
- chore: update indexed_db_futures ([#5347])
- build(deps): bump mikefarah/yq from 4.44.6 to 4.45.1 ([#5342])
- updated cosmrs and tendermint-rpc to their most recent versions ([#5339])
- build(deps): bump ts-rs from 10.0.0 to 10.1.0 ([#5338])
- build(deps): bump tempfile from 3.14.0 to 3.15.0 ([#5337])
- build(deps): bump the patch-updates group with 8 updates ([#5336])
- feature: introduce /load endpoint for self-reported quantised NymNode load ([#5326])
- feature: `CancellationToken`-based shutdowns ([#5325])
- Use expect in geodata test to give error message on failure ([#5314])
- feature: periodically remove stale gateway messages ([#5312])
- build(deps): bump the patch-updates group across 1 directory with 35 updates ([#5310])
- Add dependabot assignes for the root cargo ecosystem ([#5297])
- Move tun constants to network defaults ([#5286])
- Include IPINFO_API_TOKEN in nightly CI ([#5285])
- Nyx Chain Watcher ([#5274])
- bugfix: remove unnecessary arguments for nym-api swagger endpoints ([#5272])
- feature: nym topology revamp ([#5271])
- Add windows to CI builds ([#5269])
- http-api-client: deduplicate code ([#5267])
- build(deps): bump http from 1.1.0 to 1.2.0 ([#5228])
- NS API: add mixnet scraper ([#5200])
- build(deps): bump criterion from 0.4.0 to 0.5.1 ([#4911])
[#5417]: https://github.com/nymtech/nym/pull/5417
[#5405]: https://github.com/nymtech/nym/pull/5405
[#5404]: https://github.com/nymtech/nym/pull/5404
[#5403]: https://github.com/nymtech/nym/pull/5403
[#5397]: https://github.com/nymtech/nym/pull/5397
[#5394]: https://github.com/nymtech/nym/pull/5394
[#5390]: https://github.com/nymtech/nym/pull/5390
[#5389]: https://github.com/nymtech/nym/pull/5389
[#5388]: https://github.com/nymtech/nym/pull/5388
[#5378]: https://github.com/nymtech/nym/pull/5378
[#5376]: https://github.com/nymtech/nym/pull/5376
[#5374]: https://github.com/nymtech/nym/pull/5374
[#5361]: https://github.com/nymtech/nym/pull/5361
[#5360]: https://github.com/nymtech/nym/pull/5360
[#5358]: https://github.com/nymtech/nym/pull/5358
[#5353]: https://github.com/nymtech/nym/pull/5353
[#5347]: https://github.com/nymtech/nym/pull/5347
[#5342]: https://github.com/nymtech/nym/pull/5342
[#5339]: https://github.com/nymtech/nym/pull/5339
[#5338]: https://github.com/nymtech/nym/pull/5338
[#5337]: https://github.com/nymtech/nym/pull/5337
[#5336]: https://github.com/nymtech/nym/pull/5336
[#5326]: https://github.com/nymtech/nym/pull/5326
[#5325]: https://github.com/nymtech/nym/pull/5325
[#5314]: https://github.com/nymtech/nym/pull/5314
[#5312]: https://github.com/nymtech/nym/pull/5312
[#5310]: https://github.com/nymtech/nym/pull/5310
[#5297]: https://github.com/nymtech/nym/pull/5297
[#5286]: https://github.com/nymtech/nym/pull/5286
[#5285]: https://github.com/nymtech/nym/pull/5285
[#5274]: https://github.com/nymtech/nym/pull/5274
[#5272]: https://github.com/nymtech/nym/pull/5272
[#5271]: https://github.com/nymtech/nym/pull/5271
[#5269]: https://github.com/nymtech/nym/pull/5269
[#5267]: https://github.com/nymtech/nym/pull/5267
[#5228]: https://github.com/nymtech/nym/pull/5228
[#5200]: https://github.com/nymtech/nym/pull/5200
[#4911]: https://github.com/nymtech/nym/pull/4911
## [2025.1-reeses] (2025-01-15)
- Feture/legacy alert ([#5346])
Generated
+864 -546
View File
File diff suppressed because it is too large Load Diff
+50 -53
View File
@@ -48,13 +48,12 @@ members = [
"common/credentials-interface",
"common/crypto",
"common/dkg",
"common/ecash-double-spending",
"common/ecash-time",
"common/execute",
"common/exit-policy",
"common/gateway-requests",
"common/gateway-storage",
"common/gateway-stats-storage",
"common/gateway-storage",
"common/http-api-client",
"common/http-api-common",
"common/inclusion-probability",
@@ -93,6 +92,7 @@ members = [
"common/topology",
"common/tun",
"common/types",
"common/verloc",
"common/wasm/client-core",
"common/wasm/storage",
"common/wasm/utils",
@@ -104,6 +104,22 @@ members = [
"explorer-api/explorer-client",
"gateway",
"integrations/bity",
"nym-api",
"nym-api/nym-api-requests",
"nym-browser-extension/storage",
"nym-credential-proxy/nym-credential-proxy",
"nym-credential-proxy/nym-credential-proxy-requests",
"nym-credential-proxy/vpn-api-lib-wasm",
"nym-network-monitor",
"nym-node",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-node/nym-node-metrics",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-validator-rewarder",
"nyx-chain-watcher",
"sdk/ffi/cpp",
"sdk/ffi/go",
"sdk/ffi/shared",
@@ -112,26 +128,16 @@ members = [
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"nym-api",
"nym-api/nym-api-requests",
"nym-browser-extension/storage",
"nym-credential-proxy/nym-credential-proxy",
"nym-credential-proxy/nym-credential-proxy-requests",
"nym-credential-proxy/vpn-api-lib-wasm",
"nym-network-monitor",
"nyx-chain-watcher",
"nym-node",
"nym-node/nym-node-requests",
"nym-node/nym-node-metrics",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
"tools/internal/ssl-inject",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/mixnet-connectivity-check",
# "tools/internal/sdk-version-bump",
"tools/internal/ssl-inject",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/nym-cli",
"tools/nym-id-cli",
@@ -143,13 +149,6 @@ members = [
"wasm/mix-fetch",
"wasm/node-tester",
"wasm/zknym-lib",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"common/verloc",
"tools/internal/mixnet-connectivity-check",
]
default-members = [
@@ -173,7 +172,6 @@ exclude = [
"explorer",
"contracts",
"nym-wallet",
"nym-vpn/ui/src-tauri",
"cpu-cycles",
]
@@ -194,8 +192,9 @@ aes-gcm = "0.10.1"
aes-gcm-siv = "0.11.1"
aead = "0.5.2"
anyhow = "1.0.95"
arc-swap = "1.7.1"
argon2 = "0.5.0"
async-trait = "0.1.85"
async-trait = "0.1.86"
axum-client-ip = "0.6.1"
axum = "0.7.5"
axum-extra = "0.9.4"
@@ -216,11 +215,11 @@ chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1"
chrono = "0.4.39"
cipher = "0.4.3"
clap = "4.5.26"
clap = "4.5.28"
clap_complete = "4.5"
clap_complete_fig = "4.5"
colored = "2.0"
comfy-table = "7.1.3"
comfy-table = "7.1.4"
console = "0.15.10"
console-subscriber = "0.1.1"
console_error_panic_hook = "0.1"
@@ -249,11 +248,12 @@ futures = "0.3.31"
futures-util = "0.3"
generic-array = "0.14.7"
getrandom = "0.2.10"
getset = "0.1.3"
getset = "0.1.4"
handlebars = "3.5.5"
headers = "0.4.0"
hex = "0.4.3"
hex-literal = "0.3.3"
hickory-resolver = "0.24.3"
hkdf = "0.12.3"
hmac = "0.12.1"
http = "1"
@@ -262,9 +262,9 @@ httpcodec = "0.2.3"
humantime = "2.1.0"
humantime-serde = "1.1.1"
human-repr = "1.1.0"
hyper = "1.4.1"
hyper = "1.6.0"
hyper-util = "0.1"
indicatif = "0.17.9"
indicatif = "0.17.11"
inquire = "0.6.2"
ip_network = "0.4.1"
ipnetwork = "0.20"
@@ -282,7 +282,7 @@ moka = { version = "0.12", features = ["future"] }
nix = "0.27.1"
notify = "5.1.0"
okapi = "0.7.0"
once_cell = "1.20.2"
once_cell = "1.20.3"
opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0"
parking_lot = "0.12.3"
@@ -291,7 +291,7 @@ petgraph = "0.6.5"
pin-project = "1.1"
pin-project-lite = "0.2.16"
pretty_env_logger = "0.4.0"
publicsuffix = "2.2.3"
publicsuffix = "2.3.0"
quote = "1"
rand = "0.8.5"
rand_chacha = "0.3"
@@ -307,12 +307,12 @@ rocket_cors = "0.6.0"
rocket_okapi = "0.8.0"
safer-ffi = "0.1.13"
schemars = "0.8.21"
semver = "1.0.24"
semver = "1.0.25"
serde = "1.0.217"
serde_bytes = "0.11.15"
serde_derive = "1.0"
serde_json = "1.0.135"
serde_json_path = "0.7.1"
serde_json = "1.0.138"
serde_json_path = "0.7.2"
serde_repr = "0.1"
serde_with = "3.9.0"
serde_yaml = "0.9.25"
@@ -324,20 +324,20 @@ strum = "0.26"
strum_macros = "0.26"
subtle-encoding = "0.5"
syn = "1"
sysinfo = "0.30.13"
sysinfo = "0.33.0"
tap = "1.0.1"
tar = "0.4.43"
tempfile = "3.15"
thiserror = "1.0.64"
thiserror = "2.0"
time = "0.3.37"
tokio = "1.39"
tokio = "1.43"
tokio-stream = "0.1.17"
tokio-test = "0.4.4"
tokio-tun = "0.11.5"
tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.13"
toml = "0.8.19"
tower = "0.4.13"
toml = "0.8.20"
tower = "0.5.2"
tower-http = "0.5.2"
tracing = "0.1.41"
tracing-opentelemetry = "0.19.0"
@@ -384,22 +384,19 @@ cw4 = { version = "=1.1.2" }
cw-controllers = { version = "=1.1.0" }
# cosmrs-related
bip32 = { version = "0.5.2", default-features = false }
bip32 = { version = "0.5.3", default-features = false }
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
tendermint = "0.37.0" # same version as used by cosmrs
tendermint-rpc = "0.37.0" # same version as used by cosmrs
prost = { version = "0.12", default-features = false }
cosmrs = { version = "0.21.1" }
tendermint = "0.40.0"
tendermint-rpc = "0.40.0"
prost = { version = "0.13", default-features = false }
# wasm-related dependencies
gloo-utils = "0.2.0"
gloo-net = "0.6.0"
# TODO: migrate to 0.6+
indexed_db_futures = "0.4.2"
indexed_db_futures = "0.6.0"
js-sys = "0.3.76"
serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5"
+2 -2
View File
@@ -13,7 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* `nym-client` - an executable which you can build into your own applications. Use it for interacting with Nym nodes.
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.app)) framework.
* `nym-cli` - a tool for interacting with the network from the CLI.
<!-- coming soon
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
@@ -66,4 +66,4 @@ As a general approach, licensing is as follows this pattern:
- libraries and components are Apache 2.0 or MIT
- documentation is Apache 2.0 or CC0-1.0
Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nym.com/terms-and-conditions/operators/v1.0.0).
Nym Node Operators and Validators Terms and Conditions can be found [here](https://nym.com/operators-validators-terms).
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.46"
version = "1.1.47"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+6 -2
View File
@@ -56,7 +56,7 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
.join(DEFAULT_DATA_DIR)
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone)]
pub struct Config {
#[serde(flatten)]
pub base: BaseClientConfig,
@@ -94,6 +94,10 @@ impl CliClientConfig for Config {
}
impl Config {
pub fn base(&self) -> BaseClientConfig {
self.base.clone()
}
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
@@ -209,7 +213,7 @@ impl SocketType {
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
#[serde(default, deny_unknown_fields)]
pub struct Socket {
pub socket_type: SocketType,
@@ -107,5 +107,8 @@ enabled = {{ debug.stats_reporting.enabled }}
provider_address = '{{ debug.stats_reporting.provider_address }}'
reporting_interval = '{{ debug.stats_reporting.reporting_interval }}'
[debug.forget_me]
client = {{ debug.forget_me.client }}
stats = {{ debug.forget_me.stats }}
"#;
+8 -3
View File
@@ -20,7 +20,7 @@ pub use nym_sphinx::addressing::clients::Recipient;
pub mod config;
type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, OnDiskPersistent>;
type NativeClientBuilder = BaseClientBuilder<QueryHttpRpcNyxdClient, OnDiskPersistent>;
pub struct SocketClient {
/// Client configuration options, including, among other things, packet sending rates,
@@ -32,6 +32,10 @@ pub struct SocketClient {
}
impl SocketClient {
pub fn config(&self) -> Config {
self.config.clone()
}
pub fn new(config: Config, custom_mixnet: Option<PathBuf>) -> Self {
SocketClient {
config,
@@ -108,8 +112,9 @@ impl SocketClient {
let storage = self.initialise_storage().await?;
let user_agent = nym_bin_common::bin_info!().into();
let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client)
.with_user_agent(user_agent);
let mut base_client =
BaseClientBuilder::new(self.config().base(), storage, dkg_query_client)
.with_user_agent(user_agent);
if let Some(custom_mixnet) = &self.custom_mixnet {
base_client = base_client.with_stored_topology(custom_mixnet)?;
+1
View File
@@ -82,6 +82,7 @@ impl From<Init> for OverrideConfig {
nyxd_urls: init_config.common_args.nyxd_urls,
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
stats_reporting_address: init_config.common_args.stats_reporting_address,
forget_me: init_config.common_args.forget_me.into(),
}
}
}
+3
View File
@@ -16,6 +16,7 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client::client::Recipient;
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::config::ForgetMe;
use nym_config::OptionalSet;
use std::error::Error;
use std::net::IpAddr;
@@ -106,6 +107,7 @@ pub(crate) struct OverrideConfig {
nyxd_urls: Option<Vec<url::Url>>,
enabled_credentials_mode: Option<bool>,
stats_reporting_address: Option<Recipient>,
forget_me: ForgetMe,
}
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
@@ -133,6 +135,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
args.fastmode,
)
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
.with_base(BaseClientConfig::with_forget_me, args.forget_me)
.with_optional(Config::with_port, args.port)
.with_optional(Config::with_host, args.host)
.with_optional_custom_env_ext(
+1
View File
@@ -41,6 +41,7 @@ impl From<Run> for OverrideConfig {
nyxd_urls: run_config.common_args.nyxd_urls,
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
stats_reporting_address: run_config.common_args.stats_reporting_address,
forget_me: run_config.common_args.forget_me.into(),
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.46"
version = "1.1.47"
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"
+1
View File
@@ -93,6 +93,7 @@ impl From<Init> for OverrideConfig {
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
outfox: false,
stats_reporting_address: init_config.common_args.stats_reporting_address,
forget_me: init_config.common_args.forget_me.into(),
}
}
}
+3 -1
View File
@@ -17,7 +17,7 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
use nym_client_core::config::{GroupBy, TopologyStructure};
use nym_client_core::config::{ForgetMe, GroupBy, TopologyStructure};
use nym_config::OptionalSet;
use nym_sphinx::addressing::Recipient;
use nym_sphinx::params::{PacketSize, PacketType};
@@ -113,6 +113,7 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: Option<bool>,
outfox: bool,
stats_reporting_address: Option<Recipient>,
forget_me: ForgetMe,
}
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
@@ -179,6 +180,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
BaseClientConfig::with_topology_structure,
topology_structure,
)
.with_base(BaseClientConfig::with_forget_me, args.forget_me)
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
.with_optional(Config::with_port, args.port)
.with_optional(Config::with_ip, args.ip)
+1
View File
@@ -65,6 +65,7 @@ impl From<Run> for OverrideConfig {
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
outfox: run_config.outfox,
stats_reporting_address: run_config.common_args.stats_reporting_address,
forget_me: run_config.common_args.forget_me.into(),
}
}
}
+4
View File
@@ -113,4 +113,8 @@ enabled = {{ core.debug.stats_reporting.enabled }}
provider_address = '{{ core.debug.stats_reporting.provider_address }}'
reporting_interval = '{{ core.debug.stats_reporting.reporting_interval }}'
[core.debug.forget_me]
client = {{ core.debug.forget_me.client }}
stats = {{ core.debug.forget_me.stats }}
"#;
+1
View File
@@ -40,6 +40,7 @@ nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
nym-gateway-requests = { path = "../gateway-requests" }
nym-http-api-client = { path = "../http-api-client" }
nym-metrics = { path = "../nym-metrics" }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" }
+79 -3
View File
@@ -145,6 +145,11 @@ impl Config {
self
}
pub fn with_forget_me(mut self, forget_me: ForgetMe) -> Self {
self.debug.forget_me = forget_me;
self
}
// TODO: this should be refactored properly
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
// medium_toggle:
@@ -517,7 +522,7 @@ impl Default for Acknowledgements {
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
#[serde(default)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
@@ -553,12 +558,15 @@ pub struct Topology {
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
/// Useless without `ignore_epoch_roles = true`
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool,
/// Specifies whether this client should ignore the current epoch role of the ingress node
/// when attempting to establish new connection
pub ignore_ingress_epoch_role: bool,
}
#[allow(clippy::large_enum_variant)]
@@ -596,7 +604,9 @@ impl Default for Topology {
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
use_extended_topology: false,
ignore_egress_epoch_role: false,
ignore_egress_epoch_role: true,
ignore_ingress_epoch_role: true,
}
}
}
@@ -714,6 +724,9 @@ pub struct DebugConfig {
/// Defines all configuration options related to stats reporting.
pub stats_reporting: StatsReporting,
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMe,
}
impl DebugConfig {
@@ -736,6 +749,69 @@ impl Default for DebugConfig {
topology: Default::default(),
reply_surbs: Default::default(),
stats_reporting: Default::default(),
forget_me: Default::default(),
}
}
}
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
pub struct ForgetMe {
client: bool,
stats: bool,
}
impl From<bool> for ForgetMe {
fn from(value: bool) -> Self {
if value {
Self::new_all()
} else {
Self::new_none()
}
}
}
impl ForgetMe {
pub fn new_all() -> Self {
Self {
client: true,
stats: true,
}
}
pub fn new_client() -> Self {
Self {
client: true,
stats: false,
}
}
pub fn new_stats() -> Self {
Self {
client: false,
stats: true,
}
}
pub fn new(client: bool, stats: bool) -> Self {
Self { client, stats }
}
pub fn any(&self) -> bool {
self.client || self.stats
}
pub fn client(&self) -> bool {
self.client
}
pub fn stats(&self) -> bool {
self.stats
}
pub fn new_none() -> Self {
Self {
client: false,
stats: false,
}
}
}
@@ -182,7 +182,7 @@ impl From<ConfigV5> for Config {
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
},
stats_reporting: Default::default(),
..Default::default()
},
}
}
@@ -15,6 +15,7 @@ pub mod error;
mod manager;
mod models;
#[derive(Clone)]
pub struct OnDiskGatewaysDetails {
manager: StorageManager,
}
@@ -20,12 +20,12 @@ pub enum InMemStorageError {
MalformedGateway(#[from] BadGateway),
}
#[derive(Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct InMemGatewaysDetails {
inner: Arc<RwLock<InMemStorageInner>>,
}
#[derive(Debug, Default)]
#[derive(Clone, Debug, Default)]
struct InMemStorageInner {
active_gateway: Option<String>,
gateways: HashMap<String, GatewayRegistration>,
@@ -115,11 +115,12 @@ where
hardcoded_topology.entry_capable_nodes().cloned().collect()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways(
crate::init::helpers::gateways_for_init(
&mut rng,
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
)
.await?
};
@@ -93,6 +93,10 @@ pub struct CommonClientInitArgs {
/// Sets the address to report statistics
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub stats_reporting_address: Option<Recipient>,
/// Sets the forget me flag
#[cfg_attr(feature = "cli", clap(long, hide = true, default_value_t = false))]
pub forget_me: bool,
}
pub struct InitResultsWithConfig<T> {
@@ -170,11 +174,12 @@ where
hardcoded_topology.entry_capable_nodes().cloned().collect()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways(
crate::init::helpers::gateways_for_init(
&mut rng,
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
)
.await?
};
@@ -61,4 +61,8 @@ pub struct CommonClientRunArgs {
/// Sets the address to report statistics
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub stats_reporting_address: Option<Recipient>,
/// Sets the forget me flag
#[cfg_attr(feature = "cli", clap(long, hide = true, default_value_t = false))]
pub forget_me: bool,
}
@@ -1,6 +1,7 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::mix_traffic::ClientRequestSender;
use super::received_buffer::ReceivedBufferMessage;
use super::statistics_control::StatisticsControl;
use crate::client::base_client::storage::helpers::store_client_keys;
@@ -31,10 +32,11 @@ use crate::init::{
setup_gateway,
types::{GatewaySetup, InitialisationResult},
};
use crate::{config, spawn_future, ForgetMe};
use crate::{config, spawn_future};
use futures::channel::mpsc;
use log::*;
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_config_types::ForgetMe;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::{encryption, identity};
@@ -175,8 +177,8 @@ impl From<bool> for CredentialsToggle {
}
}
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
config: &'a Config,
pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
config: Config,
client_store: S,
dkg_query_client: Option<C>,
@@ -190,20 +192,18 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
#[cfg(unix)]
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
forget_me: ForgetMe,
}
impl<'a, C, S> BaseClientBuilder<'a, C, S>
impl<C, S> BaseClientBuilder<C, S>
where
S: MixnetClientStorage + 'static,
C: DkgQueryClient + Send + Sync + 'static,
{
pub fn new(
base_config: &'a Config,
base_config: Config,
client_store: S,
dkg_query_client: Option<C>,
) -> BaseClientBuilder<'a, C, S> {
) -> BaseClientBuilder<C, S> {
BaseClientBuilder {
config: base_config,
client_store,
@@ -216,13 +216,12 @@ where
setup_method: GatewaySetup::MustLoad { gateway_id: None },
#[cfg(unix)]
connection_fd_callback: None,
forget_me: Default::default(),
}
}
#[must_use]
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
self.forget_me = forget_me.clone();
self.config.debug.forget_me = *forget_me;
self
}
@@ -645,13 +644,12 @@ where
fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown: TaskClient,
forget_me: ForgetMe,
) -> BatchMixMessageSender {
) -> (BatchMixMessageSender, ClientRequestSender) {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) =
MixTrafficController::new(gateway_transceiver, forget_me);
let (mix_traffic_controller, mix_tx, client_tx) =
MixTrafficController::new(gateway_transceiver);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
(mix_tx, client_tx)
}
// TODO: rename it as it implies the data is persistent whilst one can use InMemBackend
@@ -773,7 +771,7 @@ where
);
let stats_reporter = Self::start_statistics_control(
self.config,
&self.config,
self.user_agent.clone(),
generate_client_stats_id(*self_address.identity()),
input_sender.clone(),
@@ -799,7 +797,7 @@ where
let gateway_transceiver = Self::setup_gateway_transceiver(
self.custom_gateway_transceiver,
self.config,
&self.config,
init_res,
bandwidth_controller,
&details_store,
@@ -833,10 +831,9 @@ where
// traffic stream.
// The MixTrafficController then sends the actual traffic
let message_sender = Self::start_mix_traffic_controller(
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
gateway_transceiver,
shutdown.fork("mix_traffic_controller"),
self.forget_me,
);
// Channels that the websocket listener can use to signal downstream to the real traffic
@@ -911,6 +908,8 @@ where
},
stats_reporter,
task_handle: shutdown,
client_request_sender,
forget_me: self.config.debug.forget_me,
})
}
}
@@ -922,6 +921,7 @@ pub struct BaseClient {
pub client_output: ClientOutputStatus,
pub client_state: ClientState,
pub stats_reporter: ClientStatsSender,
pub client_request_sender: ClientRequestSender,
pub task_handle: TaskHandle,
pub forget_me: ForgetMe,
}
@@ -4,6 +4,8 @@
// TODO: combine those more closely. Perhaps into a single underlying store.
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
use rand::rngs::OsRng;
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
use crate::client::replies::reply_storage;
use crate::client::replies::reply_storage::ReplyStorageBackend;
@@ -63,7 +65,7 @@ pub trait MixnetClientStorage {
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
}
#[derive(Default)]
#[derive(Clone)]
pub struct Ephemeral {
key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty,
@@ -71,9 +73,14 @@ pub struct Ephemeral {
gateway_details_store: InMemGatewaysDetails,
}
impl Ephemeral {
pub fn new() -> Self {
Default::default()
impl Default for Ephemeral {
fn default() -> Self {
Ephemeral {
key_store: InMemEphemeralKeys::new(&mut OsRng),
reply_store: Default::default(),
credential_store: Default::default(),
gateway_details_store: Default::default(),
}
}
}
@@ -114,6 +121,7 @@ impl MixnetClientStorage for Ephemeral {
}
}
#[derive(Clone)]
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
@@ -3,7 +3,9 @@
use crate::client::key_manager::ClientKeys;
use async_trait::async_trait;
use rand::{CryptoRng, RngCore};
use std::error::Error;
use std::sync::Arc;
use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))]
@@ -64,6 +66,7 @@ pub enum OnDiskKeysError {
},
}
#[derive(Clone)]
#[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskKeys {
paths: ClientKeysPaths,
@@ -193,9 +196,20 @@ impl KeyStore for OnDiskKeys {
}
}
#[derive(Default)]
#[derive(Clone)]
pub struct InMemEphemeralKeys {
keys: Mutex<Option<ClientKeys>>,
keys: Arc<Mutex<ClientKeys>>,
}
impl InMemEphemeralKeys {
pub fn new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
InMemEphemeralKeys {
keys: Arc::new(Mutex::new(ClientKeys::generate_new(rng))),
}
}
}
#[derive(Debug, thiserror::Error)]
@@ -208,11 +222,11 @@ impl KeyStore for InMemEphemeralKeys {
type StorageError = EphemeralKeysError;
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
Ok(self.keys.lock().await.clone())
}
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
*self.keys.lock().await = Some(keys.clone());
*self.keys.lock().await = keys.clone();
Ok(())
}
}
@@ -2,13 +2,17 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
use crate::{spawn_future, ForgetMe};
use crate::error::ClientCoreError;
use crate::spawn_future;
use log::*;
use nym_gateway_requests::ClientRequest;
use nym_sphinx::forwarding::packet::MixPacket;
use transceiver::ErasedGatewayError;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
pub type ClientRequestReceiver = tokio::sync::mpsc::Receiver<ClientRequest>;
pub type ClientRequestSender = tokio::sync::mpsc::Sender<ClientRequest>;
pub mod transceiver;
@@ -23,52 +27,67 @@ pub struct MixTrafficController {
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
mix_rx: BatchMixMessageReceiver,
client_rx: ClientRequestReceiver,
// TODO: this is temporary work-around.
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
consecutive_gateway_failure_count: usize,
forget_me: ForgetMe,
}
impl MixTrafficController {
pub fn new<T>(
gateway_transceiver: T,
forget_me: ForgetMe,
) -> (MixTrafficController, BatchMixMessageSender)
) -> (
MixTrafficController,
BatchMixMessageSender,
ClientRequestSender,
)
where
T: GatewayTransceiver + Send + 'static,
{
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
(
MixTrafficController {
gateway_transceiver: Box::new(gateway_transceiver),
mix_rx: message_receiver,
client_rx: client_receiver,
consecutive_gateway_failure_count: 0,
forget_me,
},
message_sender,
client_sender,
)
}
pub fn new_dynamic(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
forget_me: ForgetMe,
) -> (MixTrafficController, BatchMixMessageSender) {
) -> (
MixTrafficController,
BatchMixMessageSender,
ClientRequestSender,
) {
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
(
MixTrafficController {
gateway_transceiver,
mix_rx: message_receiver,
client_rx: client_receiver,
consecutive_gateway_failure_count: 0,
forget_me,
},
message_sender,
client_sender,
)
}
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
async fn on_messages(
&mut self,
mut mix_packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
debug_assert!(!mix_packets.is_empty());
let result = if mix_packets.len() == 1 {
@@ -80,21 +99,14 @@ impl MixTrafficController {
.await
};
match result {
Err(err) => {
error!("Failed to send sphinx packet(s) to the gateway: {err}");
self.consecutive_gateway_failure_count += 1;
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// todo: in the future this should initiate a 'graceful' shutdown or try
// to reconnect?
panic!("failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead. Can't do anything about it yet :(")
}
}
Ok(_) => {
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
self.consecutive_gateway_failure_count = 0;
}
if result.is_err() {
self.consecutive_gateway_failure_count += 1;
} else {
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
self.consecutive_gateway_failure_count = 0;
}
result
}
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
@@ -105,13 +117,35 @@ impl MixTrafficController {
tokio::select! {
mix_packets = self.mix_rx.recv() => match mix_packets {
Some(mix_packets) => {
self.on_messages(mix_packets).await;
if let Err(err) = self.on_messages(mix_packets).await {
error!("Failed to send sphinx packet(s) to the gateway: {err}");
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// Disconnect from the gateway. If we should try to re-connect
// is handled at a higher layer.
error!("Failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead");
// Do we need to handle the embedded mixnet client case
// separately?
shutdown.send_we_stopped(Box::new(ClientCoreError::GatewayFailedToForwardMessages));
break;
}
}
},
None => {
log::trace!("MixTrafficController: Stopping since channel closed");
break;
}
},
client_request = self.client_rx.recv() => match client_request {
Some(client_request) => {
match self.gateway_transceiver.send_client_request(client_request).await {
Ok(_) => (),
Err(e) => error!("Failed to send client request: {}", e),
};
},
None => {
log::trace!("MixTrafficController, client request channel closed");
}
},
_ = shutdown.recv_with_delay() => {
log::trace!("MixTrafficController: Received shutdown");
break;
@@ -120,25 +154,6 @@ impl MixTrafficController {
}
shutdown.recv_timeout().await;
if self.forget_me.any() {
log::info!("Sending forget me request to the gateway");
match self
.gateway_transceiver
.send_client_request(ClientRequest::ForgetMe {
client: self.forget_me.client(),
stats: self.forget_me.stats(),
})
.await
{
Ok(_) => {
log::info!("Successfully sent forget me request to the gateway");
}
Err(err) => {
log::error!("Failed to send forget me request to the gateway: {err}");
}
}
}
log::debug!("MixTrafficController: Exiting");
});
}
@@ -86,7 +86,9 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError> {
(**self).send_client_request(message).await
let _ = (**self).send_client_request(message.clone()).await?;
log::debug!("Sent client request: {:?}", message);
Ok(())
}
}
@@ -143,14 +145,7 @@ where
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError> {
if let Some(shared_key) = self.gateway_client.shared_key() {
self.gateway_client
.send_websocket_message(message.encrypt(&*shared_key)?)
.await?;
Ok(())
} else {
Err(GatewayClientError::ConnectionInInvalidState)
}
self.gateway_client.send_client_request(message).await
}
}
@@ -626,9 +626,14 @@ where
messages: Vec<RealMessage>,
transmission_lane: TransmissionLane,
) {
self.real_message_sender
if let Err(err) = self
.real_message_sender
.send((messages, transmission_lane))
.await
.expect("real message receiver task (OutQueueControl) has died");
{
error!(
"Failed to forward messages to the real message sender (OutQueueControl): {err}"
);
}
}
}
@@ -545,7 +545,7 @@ where
loop {
tokio::select! {
biased;
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("OutQueueControl: Received shutdown");
break;
}
@@ -123,6 +123,11 @@ impl StatisticsControl {
loop {
tokio::select! {
biased;
_ = task_client.recv() => {
log::trace!("StatisticsControl: Received shutdown");
break;
},
stats_event = self.stats_rx.recv() => match stats_event {
Some(stats_event) => self.stats.handle_event(stats_event),
None => {
@@ -146,13 +151,8 @@ impl StatisticsControl {
_ = local_report_interval.next() => {
self.stats.local_report(&mut task_client);
}
_ = task_client.recv_with_delay() => {
log::trace!("StatisticsControl: Received shutdown");
break;
},
}
}
task_client.recv_timeout().await;
log::debug!("StatisticsControl: Exiting");
}
+10
View File
@@ -36,6 +36,13 @@ pub enum ClientCoreError {
#[error("no gateway with id: {0}")]
NoGatewayWithId(String),
#[error("Invalid URL: {0}")]
InvalidUrl(String),
#[cfg(not(target_arch = "wasm32"))]
#[error("resolution failed: {0}")]
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
#[error("no gateways on network")]
NoGatewaysOnNetwork,
@@ -96,6 +103,9 @@ pub enum ClientCoreError {
#[error("timed out while trying to establish gateway connection")]
GatewayConnectionTimeout,
#[error("failed to forward mix messages to gateway")]
GatewayFailedToForwardMessages,
#[error("no ping measurements for the gateway ({identity}) performed")]
NoGatewayMeasurements { identity: String },
+9 -4
View File
@@ -15,6 +15,9 @@ use std::{sync::Arc, time::Duration};
use tungstenite::Message;
use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use crate::init::websockets::connect_async;
use nym_topology::NodeId;
#[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream;
@@ -23,8 +26,6 @@ use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::Instant;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket;
@@ -86,11 +87,12 @@ impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
}
}
pub async fn current_gateways<R: Rng>(
pub async fn gateways_for_init<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
user_agent: Option<UserAgent>,
minimum_performance: u8,
ignore_epoch_roles: bool,
) -> Result<Vec<RoutingNode>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
@@ -108,8 +110,11 @@ pub async fn current_gateways<R: Rng>(
log::trace!("Gateways: {:#?}", gateways);
// filter out gateways below minimum performance and ones that could operate as a mixnode
// (we don't want instability)
let valid_gateways = gateways
.iter()
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>();
@@ -128,7 +133,7 @@ pub async fn current_gateways<R: Rng>(
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
Err(_elapsed) => Err(ClientCoreError::GatewayConnectionTimeout),
Ok(Err(conn_failure)) => Err(conn_failure.into()),
Ok(Err(conn_failure)) => Err(conn_failure),
Ok(Ok((stream, _))) => Ok(stream),
}
}
+2
View File
@@ -26,6 +26,8 @@ use serde::Serialize;
pub mod helpers;
pub mod types;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) mod websockets;
// helpers for error wrapping
+44
View File
@@ -0,0 +1,44 @@
use crate::error::ClientCoreError;
use nym_http_api_client::HickoryDnsResolver;
use tokio::net::TcpStream;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
use tungstenite::handshake::client::Response;
use url::{Host, Url};
use std::net::SocketAddr;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) async fn connect_async(
endpoint: &str,
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), ClientCoreError> {
let resolver = HickoryDnsResolver::default();
let uri = Url::parse(endpoint).map_err(|_| ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
let port: u16 = uri.port_or_known_default().unwrap_or(443);
let host = uri
.host()
.ok_or(ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
// the default std resolve
let sock_addrs: Vec<SocketAddr> = match host {
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
Host::Domain(domain) => {
// Do a DNS lookup for the domain using our custom DNS resolver
resolver
.resolve_str(domain)
.await?
.into_iter()
.map(|a| SocketAddr::new(a, port))
.collect()
}
};
let stream = TcpStream::connect(&sock_addrs[..]).await?;
tokio_tungstenite::client_async_tls(endpoint, stream)
.await
.map_err(Into::into)
}
-45
View File
@@ -33,48 +33,3 @@ where
{
tokio::spawn(future);
}
#[derive(Clone, Default, Debug)]
pub struct ForgetMe {
client: bool,
stats: bool,
}
impl ForgetMe {
pub fn new_all() -> Self {
Self {
client: true,
stats: true,
}
}
pub fn new_client() -> Self {
Self {
client: true,
stats: false,
}
}
pub fn new_stats() -> Self {
Self {
client: false,
stats: true,
}
}
pub fn new(client: bool, stats: bool) -> Self {
Self { client, stats }
}
pub fn any(&self) -> bool {
self.client || self.stats
}
pub fn client(&self) -> bool {
self.client
}
pub fn stats(&self) -> bool {
self.stats
}
}
@@ -22,7 +22,7 @@ mod error;
mod manager;
mod models;
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Backend {
temporary_old_path: Option<PathBuf>,
database_path: PathBuf,
@@ -19,7 +19,7 @@ pub mod fs_backend;
#[error("no information provided")]
pub struct UndefinedError;
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Empty {
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
pub min_surb_threshold: usize,
@@ -27,6 +27,7 @@ nym-credential-storage = { path = "../../credential-storage" }
nym-credentials-interface = { path = "../../credentials-interface" }
nym-crypto = { path = "../../crypto" }
nym-gateway-requests = { path = "../../gateway-requests" }
nym-http-api-client = { path = "../../http-api-client" }
nym-network-defaults = { path = "../../network-defaults" }
nym-sphinx = { path = "../../nymsphinx" }
nym-statistics-common = { path = "../../statistics" }
@@ -40,8 +40,6 @@ use url::Url;
use std::os::fd::RawFd;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(unix))]
use std::os::raw::c_int as RawFd;
@@ -53,6 +51,11 @@ use zeroize::Zeroizing;
pub mod config;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) mod websockets;
#[cfg(not(target_arch = "wasm32"))]
use websockets::connect_async;
pub struct GatewayConfig {
pub gateway_identity: identity::PublicKey,
@@ -201,15 +204,7 @@ impl<C, St> GatewayClient<C, St> {
"Attemting to establish connection to gateway at: {}",
self.gateway_address
);
let ws_stream = match connect_async(&self.gateway_address).await {
Ok((ws_stream, _)) => ws_stream,
Err(error) => {
return Err(GatewayClientError::NetworkConnectionFailed {
address: self.gateway_address.clone(),
source: error,
})
}
};
let (ws_stream, _) = connect_async(&self.gateway_address).await?;
self.connection = SocketState::Available(Box::new(ws_stream));
@@ -271,6 +266,19 @@ impl<C, St> GatewayClient<C, St> {
}
}
pub async fn send_client_request(
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError> {
if let Some(shared_key) = self.shared_key() {
let encrypted = message.encrypt(&*shared_key)?;
Box::pin(self.send_websocket_message(encrypted)).await?;
Ok(())
} else {
Err(GatewayClientError::ConnectionInInvalidState)
}
}
async fn read_control_response(&mut self) -> Result<ServerResponse, GatewayClientError> {
// we use the fact that all request responses are Message::Text and only pushed
// sphinx packets are Message::Binary
@@ -0,0 +1,53 @@
use crate::error::GatewayClientError;
use nym_http_api_client::HickoryDnsResolver;
use tokio::net::TcpStream;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
use tungstenite::handshake::client::Response;
use url::{Host, Url};
use std::net::SocketAddr;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) async fn connect_async(
endpoint: &str,
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), GatewayClientError> {
let resolver = HickoryDnsResolver::default();
let uri =
Url::parse(endpoint).map_err(|_| GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
let port: u16 = uri.port_or_known_default().unwrap_or(443);
let host = uri
.host()
.ok_or(GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
// the default std resolve
let sock_addrs: Vec<SocketAddr> = match host {
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
Host::Domain(domain) => {
// Do a DNS lookup for the domain using our custom DNS resolver
resolver
.resolve_str(domain)
.await?
.into_iter()
.map(|a| SocketAddr::new(a, port))
.collect()
}
};
let stream = TcpStream::connect(&sock_addrs[..]).await.map_err(|error| {
GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: error.into(),
}
})?;
tokio_tungstenite::client_async_tls(endpoint, stream)
.await
.map_err(|error| GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: error,
})
}
@@ -44,7 +44,11 @@ pub enum GatewayClientError {
NetworkConnectionFailed { address: String, source: WsError },
#[error("Invalid URL: {0}")]
InvalidURL(String),
InvalidUrl(String),
#[cfg(not(target_arch = "wasm32"))]
#[error("resolution failed: {0}")]
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
#[error("No shared key was provided or obtained")]
NoSharedKeyAvailable,
@@ -11,16 +11,16 @@ use crate::{
use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationResponse,
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, SpentCredentialsResponse,
VerifyEcashTicketBody,
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, VerifyEcashTicketBody,
};
use nym_api_requests::ecash::{
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
};
use nym_api_requests::models::{
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse,
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse,
HistoricalPerformanceResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
};
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::nym_nodes::SkimmedNode;
@@ -264,6 +264,31 @@ impl<C, S> Client<C, S> {
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
}
pub async fn get_full_node_performance_history(
&self,
node_id: NodeId,
) -> Result<Vec<HistoricalPerformanceResponse>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut history = Vec::new();
loop {
let mut res = self
.nym_api
.get_node_performance_history(node_id, Some(page), None)
.await?;
history.append(&mut res.history.data);
if history.len() < res.history.pagination.total {
page += 1
} else {
break;
}
}
Ok(history)
}
// TODO: combine with NymApiClient...
pub async fn get_all_cached_described_nodes(
&self,
@@ -621,13 +646,6 @@ impl NymApiClient {
.await?)
}
#[deprecated]
pub async fn spent_credentials_filter(
&self,
) -> Result<SpentCredentialsResponse, ValidatorClientError> {
Ok(self.nym_api.double_spending_filter_v1().await?)
}
pub async fn partial_expiration_date_signatures(
&self,
expiration_date: Option<Date>,
@@ -65,6 +65,12 @@ pub enum EcashApiError {
#[from]
source: cosmrs::ErrorReport,
},
#[error("nym api error")]
NymApi {
#[from]
source: crate::ValidatorClientError,
},
}
impl TryFrom<ContractVKShare> for EcashApiClient {
@@ -13,7 +13,7 @@ use nym_api_requests::ecash::models::{
use nym_api_requests::ecash::VerificationKeyResponse;
use nym_api_requests::models::{
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
NodeRefreshBody, NymNodeDescription, RewardedSetResponse,
NodeRefreshBody, NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse,
};
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
use nym_api_requests::pagination::PaginatedResponse;
@@ -31,6 +31,7 @@ pub use nym_api_requests::{
StakeSaturationResponse, UptimeResponse,
},
nym_nodes::{CachedNodesResponse, SkimmedNode},
NymNetworkDetailsResponse,
};
pub use nym_coconut_dkg_common::types::EpochId;
use nym_contracts_common::IdentityKey;
@@ -163,6 +164,35 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[tracing::instrument(level = "debug", skip_all)]
async fn get_node_performance_history(
&self,
node_id: NodeId,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<PerformanceHistoryResponse, NymAPIError> {
let mut params = Vec::new();
if let Some(page) = page {
params.push(("page", page.to_string()))
}
if let Some(per_page) = per_page {
params.push(("per_page", per_page.to_string()))
}
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_PERFORMANCE_HISTORY,
&*node_id.to_string(),
],
&params,
)
.await
}
#[tracing::instrument(level = "debug", skip_all)]
async fn get_nodes_described(
&self,
@@ -179,8 +209,15 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
self.get_json(&[routes::API_VERSION, "nym-nodes", "described"], &params)
.await
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_DESCRIBED,
],
&params,
)
.await
}
#[tracing::instrument(level = "debug", skip_all)]
@@ -199,8 +236,15 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
self.get_json(&[routes::API_VERSION, "nym-nodes", "bonded"], &params)
.await
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_BONDED,
],
&params,
)
.await
}
#[deprecated]
@@ -210,7 +254,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
"nym-nodes",
routes::NYM_NODES_ROUTES,
"mixnodes",
"skimmed",
],
@@ -226,7 +270,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
"nym-nodes",
routes::NYM_NODES_ROUTES,
"gateways",
"skimmed",
],
@@ -238,7 +282,11 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))]
async fn get_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
self.get_json(
&[routes::API_VERSION, "nym-nodes", "rewarded-set"],
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_REWARDED_SET,
],
NO_PARAMS,
)
.await
@@ -271,7 +319,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
"nym-nodes",
routes::NYM_NODES_ROUTES,
"skimmed",
"entry-gateways",
"all",
@@ -308,7 +356,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
"nym-nodes",
routes::NYM_NODES_ROUTES,
"skimmed",
"mixnodes",
"active",
@@ -345,7 +393,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
"nym-nodes",
routes::NYM_NODES_ROUTES,
"skimmed",
"mixnodes",
"all",
@@ -377,7 +425,12 @@ pub trait NymApiClientExt: ApiClient {
}
self.get_json(
&[routes::API_VERSION, "unstable", "nym-nodes", "skimmed"],
&[
routes::API_VERSION,
"unstable",
routes::NYM_NODES_ROUTES,
"skimmed",
],
&params,
)
.await
@@ -686,8 +739,8 @@ pub trait NymApiClientExt: ApiClient {
self.get_json(
&[
routes::API_VERSION,
"nym-nodes",
"performance",
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_PERFORMANCE,
&node_id.to_string(),
],
NO_PARAMS,
@@ -702,8 +755,8 @@ pub trait NymApiClientExt: ApiClient {
self.get_json(
&[
routes::API_VERSION,
"nym-nodes",
"annotation",
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_ANNOTATION,
&node_id.to_string(),
],
NO_PARAMS,
@@ -797,20 +850,6 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn double_spending_filter_v1(&self) -> Result<SpentCredentialsResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::ECASH_ROUTES,
routes::DOUBLE_SPENDING_FILTER_V1,
],
NO_PARAMS,
)
.await
}
#[instrument(level = "debug", skip(self))]
async fn partial_expiration_date_signatures(
&self,
@@ -927,7 +966,11 @@ pub trait NymApiClientExt: ApiClient {
request: &NodeRefreshBody,
) -> Result<(), NymAPIError> {
self.post_json(
&[routes::API_VERSION, "nym-nodes", "refresh-described"],
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_REFRESH_DESCRIBED,
],
NO_PARAMS,
request,
)
@@ -971,6 +1014,15 @@ pub trait NymApiClientExt: ApiClient {
)
.await
}
#[instrument(level = "debug", skip(self))]
async fn get_network_details(&self) -> Result<NymNetworkDetailsResponse, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::NETWORK, routes::DETAILS],
NO_PARAMS,
)
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -13,8 +13,6 @@ pub const DETAILED: &str = "detailed";
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
pub const ACTIVE: &str = "active";
pub const REWARDED: &str = "rewarded";
pub const DOUBLE_SPENDING_FILTER_V1: &str = "double-spending-filter-v1";
pub const ECASH_ROUTES: &str = "ecash";
pub use ecash::*;
@@ -34,6 +32,19 @@ pub mod ecash {
pub const EPOCH_ID_PARAM: &str = "epoch_id";
}
pub const NYM_NODES_ROUTES: &str = "nym-nodes";
pub use nym_nodes::*;
pub mod nym_nodes {
pub const NYM_NODES_PERFORMANCE_HISTORY: &str = "performance-history";
pub const NYM_NODES_PERFORMANCE: &str = "performance";
pub const NYM_NODES_ANNOTATION: &str = "annotation";
pub const NYM_NODES_DESCRIBED: &str = "described";
pub const NYM_NODES_BONDED: &str = "bonded";
pub const NYM_NODES_REWARDED_SET: &str = "rewarded-set";
pub const NYM_NODES_REFRESH_DESCRIBED: &str = "refresh-described";
}
pub const STATUS_ROUTES: &str = "status";
pub const API_STATUS_ROUTES: &str = "api-status";
pub const HEALTH: &str = "health";
@@ -54,6 +65,8 @@ pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
pub const SUBMIT_GATEWAY: &str = "submit-gateway-monitoring-results";
pub const SUBMIT_NODE: &str = "submit-node-monitoring-results";
pub const PERFORMANCE: &str = "performance";
pub const SERVICE_PROVIDERS: &str = "services";
pub const DETAILS: &str = "details";
pub const NETWORK: &str = "network";
@@ -153,13 +153,20 @@ pub trait CosmWasmClient: TendermintRpcClient {
let req = QueryAllBalancesRequest {
address: address.to_string(),
pagination,
resolve_denom: false,
};
let mut res = self
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
.await?;
let early_break = res.balances.is_empty();
raw_balances.append(&mut res.balances);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -187,7 +194,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
.await?;
let early_break = res.supply.is_empty();
supply.append(&mut res.supply);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -328,7 +341,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
.await?;
let early_break = res.code_infos.is_empty();
raw_codes.append(&mut res.code_infos);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -373,7 +392,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
.await?;
let early_break = res.contracts.is_empty();
raw_contracts.append(&mut res.contracts);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -429,7 +454,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
.await?;
let early_break = res.entries.is_empty();
raw_entries.append(&mut res.entries);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -4,9 +4,11 @@
use crate::rpc::TendermintRpcClient;
use async_trait::async_trait;
use base64::Engine;
use cosmrs::tendermint;
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
use reqwest::header::HeaderMap;
use reqwest::{header, RequestBuilder};
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
use tendermint_rpc::{
client::CompatMode,
dialect::{self, Dialect},
@@ -21,8 +23,21 @@ macro_rules! perform_with_compat {
($self:expr, $request:expr) => {{
let request = $request;
match $self.compat {
CompatMode::V0_37 => $self.perform_v0_37(request).await,
CompatMode::V0_34 => $self.perform_v0_34(request).await,
CompatMode::V0_38 => {
$self
.perform_request_with_dialect(request, dialect::v0_38::Dialect)
.await
}
CompatMode::V0_37 => {
$self
.perform_request_with_dialect(request, dialect::v0_37::Dialect)
.await
}
CompatMode::V0_34 => {
$self
.perform_request_with_dialect(request, dialect::v0_34::Dialect)
.await
}
}
}};
}
@@ -70,7 +85,11 @@ impl ReqwestRpcClient {
.headers(headers)
}
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
async fn perform_request_with_dialect<R, S>(
&self,
request: R,
_dialect: S,
) -> Result<R::Output, Error>
where
R: SimpleRequest<S>,
S: Dialect,
@@ -81,26 +100,25 @@ impl ReqwestRpcClient {
.send()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
let response_status = response.status();
let bytes = response
.bytes()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
// Successful JSON-RPC requests are expected to return a 200 OK HTTP status.
// Otherwise, this means that the HTTP request failed as a whole,
// as opposed to the JSON-RPC request returning an error,
// and we cannot expect the response body to be a valid JSON-RPC response.
if response_status != reqwest::StatusCode::OK {
// hehe, that's so nasty but we have to somehow convert between different versions of the same lib
return Err(Error::http_request_failed(
response_status.as_u16().try_into().unwrap(),
));
}
R::Response::from_string(bytes).map(Into::into)
}
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_34::Dialect>,
{
self.perform_request(request).await
}
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_37::Dialect>,
{
self.perform_request(request).await
}
}
trait TendermintRpcErrorMap {
@@ -120,18 +138,50 @@ impl TendermintRpcClient for ReqwestRpcClient {
where
R: SimpleRequest,
{
self.perform_request(request).await
self.perform_request_with_dialect(request, LatestDialect)
.await
}
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
async fn block<H>(&self, height: H) -> Result<endpoint::block::Response, Error>
where
H: Into<Height> + Send,
{
perform_with_compat!(self, block_results::Request::new(height.into()))
perform_with_compat!(self, endpoint::block::Request::new(height.into()))
}
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
perform_with_compat!(self, block_results::Request::default())
async fn block_by_hash(
&self,
hash: tendermint::Hash,
) -> Result<endpoint::block_by_hash::Response, Error> {
perform_with_compat!(self, endpoint::block_by_hash::Request::new(hash))
}
async fn latest_block(&self) -> Result<endpoint::block::Response, Error> {
perform_with_compat!(self, endpoint::block::Request::default())
}
async fn block_results<H>(&self, height: H) -> Result<endpoint::block_results::Response, Error>
where
H: Into<Height> + Send,
{
perform_with_compat!(self, endpoint::block_results::Request::new(height.into()))
}
async fn latest_block_results(&self) -> Result<endpoint::block_results::Response, Error> {
perform_with_compat!(self, endpoint::block_results::Request::default())
}
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<endpoint::block_search::Response, Error> {
perform_with_compat!(
self,
endpoint::block_search::Request::new(query, page, per_page, order)
)
}
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
@@ -140,11 +190,26 @@ impl TendermintRpcClient for ReqwestRpcClient {
{
let height = height.into();
match self.compat {
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
CompatMode::V0_38 => {
self.perform_request_with_dialect(
endpoint::header::Request::new(height),
v0_38::Dialect,
)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(
endpoint::header::Request::new(height),
v0_37::Dialect,
)
.await
}
CompatMode::V0_34 => {
// Back-fill with a request to /block endpoint and
// taking just the header from the response.
let resp = self.perform_v0_34(block::Request::new(height)).await?;
let resp = self
.perform_request_with_dialect(block::Request::new(height), v0_34::Dialect)
.await?;
Ok(resp.into())
}
}
@@ -152,12 +217,25 @@ impl TendermintRpcClient for ReqwestRpcClient {
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
match self.compat {
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
CompatMode::V0_38 => {
self.perform_request_with_dialect(
header_by_hash::Request::new(hash),
v0_38::Dialect,
)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(
header_by_hash::Request::new(hash),
v0_37::Dialect,
)
.await
}
CompatMode::V0_34 => {
// Back-fill with a request to /block_by_hash endpoint and
// taking just the header from the response.
let resp = self
.perform_v0_34(block_by_hash::Request::new(hash))
.perform_request_with_dialect(block_by_hash::Request::new(hash), v0_34::Dialect)
.await?;
Ok(resp.into())
}
@@ -167,8 +245,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
/// `/broadcast_evidence`: broadcast an evidence.
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
match self.compat {
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
CompatMode::V0_38 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_38::Dialect)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_37::Dialect)
.await
}
CompatMode::V0_34 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_34::Dialect)
.await
}
}
}
@@ -113,6 +113,10 @@ impl Role {
pub fn is_standby(&self) -> bool {
matches!(self, Role::Standby)
}
pub fn is_mixnode(&self) -> bool {
matches!(self, Role::Layer1 | Role::Layer2 | Role::Layer3)
}
}
impl Display for Role {
@@ -8,81 +8,81 @@ use thiserror::Error;
#[derive(Error, Debug, PartialEq)]
pub enum VestingContractError {
#[error("VESTING ({}): {0}", line!())]
#[error("VESTING ({l}): {0}", l = line!())]
Std(#[from] StdError),
#[error("VESTING: {0}")]
OverflowError(#[from] OverflowError),
#[error("VESTING ({}): Account does not exist - {0}", line!())]
#[error("VESTING ({l}): Account does not exist - {0}", l = line!())]
NoAccountForAddress(String),
#[error("VESTING ({}): Only admin can perform this action, {0} is not admin", line!())]
#[error("VESTING ({l}): Only admin can perform this action, {0} is not admin", l = line!())]
NotAdmin(String),
#[error("VESTING ({}): Balance not found for existing account ({0}), this is a bug", line!())]
#[error("VESTING ({l}): Balance not found for existing account ({0}), this is a bug", l = line!())]
NoBalanceForAddress(String),
#[error("VESTING ({}): Insufficient balance for address {0} -> {1}", line!())]
#[error("VESTING ({l}): Insufficient balance for address {0} -> {1}", l = line!())]
InsufficientBalance(String, u128),
#[error("VESTING ({}): Insufficient spendable balance for address {0} -> {1}", line!())]
#[error("VESTING ({l}): Insufficient spendable balance for address {0} -> {1}", l = line!())]
InsufficientSpendable(String, u128),
#[error(
"VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
, line!())]
"VESTING ({l}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
, l = line!())]
NotDelegate(String),
#[error("VESTING ({}): Total vesting amount is inprobably low -> {0}, this is likely an error", line!())]
#[error("VESTING ({l}): Total vesting amount is inprobably low -> {0}, this is likely an error", l = line!())]
ImprobableVestingAmount(u128),
#[error("VESTING ({}): Address {0} has already bonded a node", line!())]
#[error("VESTING ({l}): Address {0} has already bonded a node", l = line!())]
AlreadyBonded(String),
#[error("VESTING ({}): Received empty funds vector", line!())]
#[error("VESTING ({l}): Received empty funds vector", l = line!())]
EmptyFunds,
#[error("VESTING ({}): Received wrong denom: {0}, expected {1}", line!())]
#[error("VESTING ({l}): Received wrong denom: {0}, expected {1}", l = line!())]
WrongDenom(String, String),
#[error("VESTING ({}): Received multiple denoms, expected 1", line!())]
#[error("VESTING ({l}): Received multiple denoms, expected 1", l = line!())]
MultipleDenoms,
#[error("VESTING ({}): No delegations found for account {0}, mix_identity {1}", line!())]
#[error("VESTING ({l}): No delegations found for account {0}, mix_identity {1}", l = line!())]
NoSuchDelegation(Addr, NodeId),
#[error("VESTING ({}): Only mixnet contract can perform this operation, got {0}", line!())]
#[error("VESTING ({l}): Only mixnet contract can perform this operation, got {0}", l = line!())]
NotMixnetContract(Addr),
#[error("VESTING ({}): Calculation underflowed", line!())]
#[error("VESTING ({l}): Calculation underflowed", l = line!())]
Underflow,
#[error("VESTING ({}): No bond found for account {0}", line!())]
#[error("VESTING ({l}): No bond found for account {0}", l = line!())]
NoBondFound(String),
#[error("VESTING: Attempted to reduce mixnode bond pledge below zero! The current pledge is {current} and we attempted to reduce it by {decrease_by}.")]
InvalidBondPledgeReduction { current: Coin, decrease_by: Coin },
#[error("VESTING ({}): Action can only be executed by account owner -> {0}", line!())]
#[error("VESTING ({l}): Action can only be executed by account owner -> {0}", l = line!())]
NotOwner(String),
#[error("VESTING ({}): Invalid address: {0}", line!())]
#[error("VESTING ({l}): Invalid address: {0}", l = line!())]
InvalidAddress(String),
#[error("VESTING ({}): Account already exists: {0}", line!())]
#[error("VESTING ({l}): Account already exists: {0}", l = line!())]
AccountAlreadyExists(String),
#[error("VESTING ({}): Staking account already exists: {0}", line!())]
#[error("VESTING ({l}): Staking account already exists: {0}", l = line!())]
StakingAccountAlreadyExists(String),
#[error("VESTING ({}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", line!())]
#[error("VESTING ({l}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", l = line!())]
MinVestingFunds { sent: u128, need: u128 },
#[error("VESTING ({}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", line!())]
#[error("VESTING ({l}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", l = line!())]
LockedPledgeCapReached { current: Uint128, cap: Uint128 },
#[error("VESTING: ({}: Account owned by {owner} has unpopulated vesting periods!", line!())]
#[error("VESTING: ({l}: Account owned by {owner} has unpopulated vesting periods!", l = line!())]
UnpopulatedVestingPeriods { owner: Addr },
#[error("VESTING: Vesting account associated with {0} already exists, only addresses with not existing vesting accounts can be added as staking addresses")]
+1 -1
View File
@@ -19,7 +19,7 @@ use std::error::Error;
// `SELECT total_tickets, used_tickets FROM ecash_ticketbook WHERE expiration_date >= ?`, today_date
// then for each calculate the diff total_tickets - used_tickets and multiply the result by the size of the ticket
#[async_trait]
pub trait Storage: Send + Sync {
pub trait Storage: Clone + Send + Sync {
type StorageError: Error;
async fn close(&self);
@@ -26,7 +26,6 @@ nym-api-requests = { path = "../../nym-api/nym-api-requests" }
nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-double-spending = { path = "../ecash-double-spending" }
nym-gateway-requests = { path = "../gateway-requests" }
nym-gateway-storage = { path = "../gateway-storage" }
nym-task = { path = "../task" }
@@ -13,6 +13,7 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
use nym_credentials_interface::Bandwidth;
use nym_credentials_interface::{ClientTicket, TicketType};
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
@@ -352,7 +353,9 @@ impl CredentialHandler {
}
Err(err) => {
error!("failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later");
Ok(false)
Err(EcashTicketError::ApiFailure(EcashApiError::NymApi {
source: err,
}))
}
}
}
-14
View File
@@ -1,14 +0,0 @@
[package]
name = "nym-ecash-double-spending"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
bit-vec = { workspace = true }
bloomfilter = { workspace = true }
nym-network-defaults = { path = "../network-defaults" }
-136
View File
@@ -1,136 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bit_vec::BitVec;
use bloomfilter::Bloom;
use nym_network_defaults::{BloomfilterParameters, ECASH_DS_BLOOMFILTER_PARAMS};
pub struct DoubleSpendingFilter {
params: BloomfilterParameters,
inner: Bloom<Vec<u8>>,
}
impl Default for DoubleSpendingFilter {
fn default() -> Self {
DoubleSpendingFilter::new_empty_ecash()
}
}
pub fn bloom_from_params<T>(params: &BloomfilterParameters, bitvec: BitVec) -> Bloom<Vec<T>> {
assert_eq!(params.bitmap_size, bitvec.len() as u64);
Bloom::from_bit_vec(
bitvec,
params.bitmap_size,
params.num_hashes,
params.sip_keys,
)
}
impl DoubleSpendingFilter {
pub fn new_empty(params: BloomfilterParameters) -> Self {
let bitvec = BitVec::from_elem(params.bitmap_size as usize, false);
DoubleSpendingFilter {
inner: bloom_from_params(&params, bitvec),
params,
}
}
pub fn params(&self) -> BloomfilterParameters {
self.params
}
pub fn rebuild(&self) -> DoubleSpendingFilterBuilder {
DoubleSpendingFilterBuilder::new(self.params)
}
pub fn reset(&mut self) {
self.inner.clear()
}
pub fn new_empty_ecash() -> Self {
DoubleSpendingFilter::new_empty(ECASH_DS_BLOOMFILTER_PARAMS)
}
pub fn builder(params: BloomfilterParameters) -> DoubleSpendingFilterBuilder {
DoubleSpendingFilterBuilder::new(params)
}
pub fn from_bytes(params: BloomfilterParameters, bitmap: &[u8]) -> Self {
DoubleSpendingFilter {
inner: bloom_from_params(&params, BitVec::from_bytes(bitmap)),
params,
}
}
pub fn replace_bitvec(&mut self, new: BitVec) {
self.inner = bloom_from_params(&self.params, new)
}
pub fn dump_bitmap(&self) -> Vec<u8> {
self.inner.bitmap()
}
pub fn set(&mut self, b: &Vec<u8>) {
self.inner.set(b);
}
pub fn check(&self, b: &Vec<u8>) -> bool {
self.inner.check(b)
}
}
pub struct DoubleSpendingFilterBuilder {
params: BloomfilterParameters,
bit_vec_builder: Option<BitVecBuilder>,
}
impl DoubleSpendingFilterBuilder {
pub fn new(params: BloomfilterParameters) -> Self {
DoubleSpendingFilterBuilder {
params,
bit_vec_builder: None,
}
}
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
match &mut self.bit_vec_builder {
None => {
self.bit_vec_builder = Some(BitVecBuilder::new(b));
true
}
Some(builder) => builder.add_bytes(b),
}
}
pub fn build(self) -> DoubleSpendingFilter {
match self.bit_vec_builder {
None => DoubleSpendingFilter::new_empty(self.params),
Some(builder) => DoubleSpendingFilter {
inner: bloom_from_params(&self.params, builder.finish()),
params: self.params,
},
}
}
}
pub struct BitVecBuilder(BitVec);
impl BitVecBuilder {
pub fn new(initial_bitmap: &[u8]) -> Self {
BitVecBuilder(BitVec::from_bytes(initial_bitmap))
}
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
let add = BitVec::from_bytes(b);
if self.0.len() != add.len() {
return false;
}
self.0.or(&add);
true
}
pub fn finish(self) -> BitVec {
self.0
}
}
+6 -6
View File
@@ -59,12 +59,12 @@ pub enum GatewayRequestsError {
source: NymNodeRoutingAddressError,
},
#[error("received request had invalid size. (actual: {0}, but expected one of: {} (ACK), {} (REGULAR), {}, {}, {} (EXTENDED))",
PacketSize::AckPacket.size(),
PacketSize::RegularPacket.size(),
PacketSize::ExtendedPacket8.size(),
PacketSize::ExtendedPacket16.size(),
PacketSize::ExtendedPacket32.size())
#[error("received request had invalid size. (actual: {0}, but expected one of: {a} (ACK), {r} (REGULAR), {e8}, {e16}, {e32} (EXTENDED))",
a = PacketSize::AckPacket.size(),
r = PacketSize::RegularPacket.size(),
e8 = PacketSize::ExtendedPacket8.size(),
e16 = PacketSize::ExtendedPacket16.size(),
e32 = PacketSize::ExtendedPacket32.size())
]
RequestOfInvalidSize(usize),
@@ -13,7 +13,7 @@ use std::str::FromStr;
use tungstenite::Message;
// wrapper for all encrypted requests for ease of use
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[non_exhaustive]
pub enum ClientRequest {
UpgradeKey {
@@ -3,5 +3,22 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
ALTER TABLE message_store
ADD COLUMN timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP;
RENAME TO message_store_old;
-- add new column with message timestamp.
-- note: we can't simply alter existing table to add it since the default value is non-constant
CREATE TABLE message_store
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
client_address_bs58 TEXT NOT NULL,
content BLOB NOT NULL,
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO message_store(id, client_address_bs58, content)
SELECT id, client_address_bs58, content
FROM message_store_old;
DROP TABLE message_store_old;
+7
View File
@@ -15,6 +15,7 @@ async-trait = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
http.workspace = true
url = { workspace = true }
once_cell = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
@@ -22,7 +23,13 @@ tracing = { workspace = true }
nym-bin-common = { path = "../bin-common" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
hickory-resolver = { workspace = true, features = ["dns-over-https-rustls", "webpki-roots"] }
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
features = ["tokio"]
[dev-dependencies]
tokio = { workspace = true, features=["rt", "macros"] }
+178
View File
@@ -0,0 +1,178 @@
//! DNS resolver configuration for internal lookups.
//!
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
//! supporting DoH and DoT.
//!
//! This resolver implements a fallback mechanism where, should the DNS-over-TLS resolution fail, a
//! followup resolution will be done using the hosts configured default (e.g. `/etc/resolve.conf` on
//! linux).
//!
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
//! `hickory-resolver` crate
#![deny(missing_docs)]
use crate::ClientBuilder;
use std::{net::SocketAddr, sync::Arc};
use hickory_resolver::lookup_ip::LookupIp;
use hickory_resolver::{
config::{LookupIpStrategy, NameServerConfigGroup, ResolverConfig, ResolverOpts},
error::ResolveError,
lookup_ip::LookupIpIntoIter,
TokioAsyncResolver,
};
use once_cell::sync::OnceCell;
use reqwest::dns::{Addrs, Name, Resolve, Resolving};
use tracing::warn;
impl ClientBuilder {
/// Override the DNS resolver implementation used by the underlying http client.
pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> Self {
self.reqwest_client_builder = self.reqwest_client_builder.dns_resolver(resolver);
self
}
}
struct SocketAddrs {
iter: LookupIpIntoIter,
}
#[derive(Debug, thiserror::Error)]
#[error("hickory-dns resolver error: {hickory_error}")]
/// Error occurring while resolving a hostname into an IP address.
pub struct HickoryDnsError {
#[from]
hickory_error: ResolveError,
}
/// Wrapper around an `AsyncResolver`, which implements the `Resolve` trait.
#[derive(Debug, Default, Clone)]
pub struct HickoryDnsResolver {
/// Since we might not have been called in the context of a
/// Tokio Runtime in initialization, so we must delay the actual
/// construction of the resolver.
state: Arc<OnceCell<TokioAsyncResolver>>,
fallback: Arc<OnceCell<TokioAsyncResolver>>,
}
impl Resolve for HickoryDnsResolver {
fn resolve(&self, name: Name) -> Resolving {
let resolver = self.state.clone();
let fallback = self.fallback.clone();
Box::pin(async move {
let resolver = resolver.get_or_try_init(new_resolver)?;
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
let lookup = match resolver.lookup_ip(name.as_str()).await {
Ok(res) => res,
Err(e) => {
// on failure use the fall back system configured DNS resolver
warn!("primary DNS failed w/ error {e}: using system fallback");
let resolver = fallback.get_or_try_init(new_resolver_system)?;
resolver.lookup_ip(name.as_str()).await?
}
};
let addrs: Addrs = Box::new(SocketAddrs {
iter: lookup.into_iter(),
});
Ok(addrs)
})
}
}
impl Iterator for SocketAddrs {
type Item = SocketAddr;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
}
}
impl HickoryDnsResolver {
/// Attempt to resolve a domain name to a set of ['IpAddr']s
pub async fn resolve_str(&self, name: &str) -> Result<LookupIp, HickoryDnsError> {
let resolver = self.state.get_or_try_init(new_resolver)?;
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
let lookup = match resolver.lookup_ip(name).await {
Ok(res) => res,
Err(e) => {
// on failure use the fall back system configured DNS resolver
warn!("primary DNS failed w/ error {e}: using system fallback");
let resolver = self.fallback.get_or_try_init(new_resolver_system)?;
resolver.lookup_ip(name).await?
}
};
Ok(lookup)
}
}
/// Create a new resolver with a custom DoT based configuration. The options are overridden to look
/// up for both IPv4 and IPv6 addresses to work with "happy eyeballs" algorithm.
fn new_resolver() -> Result<TokioAsyncResolver, HickoryDnsError> {
let mut name_servers = NameServerConfigGroup::google_tls();
name_servers.merge(NameServerConfigGroup::google_https());
// name_servers.merge(NameServerConfigGroup::google_h3());
name_servers.merge(NameServerConfigGroup::quad9_tls());
name_servers.merge(NameServerConfigGroup::quad9_https());
name_servers.merge(NameServerConfigGroup::cloudflare_tls());
name_servers.merge(NameServerConfigGroup::cloudflare_https());
let config = ResolverConfig::from_parts(None, Vec::new(), name_servers);
let mut opts = ResolverOpts::default();
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
// Would like to enable this when 0.25 stabilizes
// opts.server_ordering_strategy = ServerOrderingStrategy::RoundRobin;
Ok(TokioAsyncResolver::tokio(config, opts))
}
/// Create a new resolver with the default configuration, which reads from the system DNS config
/// (i.e. `/etc/resolve.conf` in unix). The options are overridden to look up for both IPv4 and IPv6
/// addresses to work with "happy eyeballs" algorithm.
fn new_resolver_system() -> Result<TokioAsyncResolver, HickoryDnsError> {
let (config, mut opts) = hickory_resolver::system_conf::read_system_conf()?;
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
Ok(TokioAsyncResolver::tokio(config, opts))
}
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
async fn reqwest_hickory_doh() {
let resolver = HickoryDnsResolver::default();
let client = reqwest::ClientBuilder::new()
.dns_resolver(resolver.into())
.build()
.unwrap();
let resp = client
.get("http://ifconfig.me:80")
.send()
.await
.unwrap()
.bytes()
.await
.unwrap();
assert!(!resp.is_empty());
}
#[tokio::test]
async fn dns_lookup() -> Result<(), HickoryDnsError> {
let resolver = HickoryDnsResolver::default();
let domain = "ifconfig.me";
let addrs = resolver.resolve_str(domain).await?;
assert!(addrs.into_iter().next().is_some());
Ok(())
}
}
+18 -5
View File
@@ -6,17 +6,23 @@ use reqwest::header::HeaderValue;
use reqwest::{RequestBuilder, Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error;
use tracing::{instrument, warn};
use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::Arc;
use std::{fmt::Display, time::Duration};
pub use reqwest::IntoUrl;
mod user_agent;
pub use user_agent::UserAgent;
mod user_agent;
#[cfg(not(target_arch = "wasm32"))]
mod dns;
#[cfg(not(target_arch = "wasm32"))]
pub use dns::{HickoryDnsError, HickoryDnsResolver};
// The timeout is relatively high as we are often making requests over the mixnet, where latency is
// high and chatty protocols take a while to complete.
@@ -86,11 +92,18 @@ impl ClientBuilder {
// TODO: or should we maybe default to https?
Self::new(alt)
} else {
#[cfg(target_arch = "wasm32")]
let reqwest_client_builder = reqwest::ClientBuilder::new();
#[cfg(not(target_arch = "wasm32"))]
let reqwest_client_builder =
reqwest::ClientBuilder::new().dns_resolver(Arc::new(HickoryDnsResolver::default()));
Ok(ClientBuilder {
url: url.into_url()?,
timeout: None,
custom_user_agent: false,
reqwest_client_builder: reqwest::ClientBuilder::new(),
reqwest_client_builder,
})
}
}
@@ -755,7 +768,7 @@ where
E: DeserializeOwned + Display,
{
let status = res.status();
tracing::debug!("Status: {} (success: {})", &status, status.is_success());
tracing::trace!("Status: {} (success: {})", &status, status.is_success());
if !allow_empty {
if let Some(0) = res.content_length() {
@@ -30,6 +30,10 @@ pub struct Config {
}
impl Config {
pub fn base(&self) -> BaseClientConfig {
self.base.clone()
}
pub fn new<S: Into<String>>(id: S, version: S, provider_mix_address: S) -> Self {
Config {
base: BaseClientConfig::new(id, version),
+7 -3
View File
@@ -76,6 +76,10 @@ where
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
{
pub fn config(&self) -> Config {
self.config.clone()
}
pub fn new(
config: Config,
storage: S,
@@ -233,7 +237,7 @@ where
};
let mut base_builder =
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client)
BaseClientBuilder::new(self.config.base(), self.storage, dkg_query_client)
.with_gateway_setup(self.setup_method)
.with_user_agent(self.user_agent);
@@ -241,7 +245,7 @@ where
base_builder = base_builder.with_stored_topology(custom_mixnet)?;
}
let packet_type = self.config.base.debug.traffic.packet_type;
let packet_type = self.config.base().debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
let self_address = started_client.address;
let client_input = started_client.client_input.register_producer();
@@ -252,7 +256,7 @@ where
Self::start_socks5_listener(
&self.config.socks5,
self.config.base.debug,
self.config.base().debug,
client_input,
client_output,
client_state,
+1 -1
View File
@@ -67,7 +67,7 @@ impl Default for ClientStatsReport {
pub struct OsInformation {
pub(crate) os_type: String,
pub(crate) os_version: Option<String>,
pub(crate) os_arch: Option<String>,
pub(crate) os_arch: String,
}
impl OsInformation {
+2 -2
View File
@@ -221,7 +221,7 @@ impl TaskManager {
}
}
pub(crate) async fn wait_for_graceful_shutdown(&mut self) {
pub async fn wait_for_graceful_shutdown(&mut self) {
if let Some(notify_rx) = self.notify_rx.take() {
drop(notify_rx);
}
@@ -315,7 +315,7 @@ impl TaskClient {
const MAX_NAME_LENGTH: usize = 128;
const OVERFLOW_NAME: &'static str = "reached maximum TaskClient children name depth";
const SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT: Duration = Duration::from_secs(5);
const SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT: Duration = Duration::from_secs(10);
fn new(
notify: watch::Receiver<()>,
+10 -13
View File
@@ -11,7 +11,7 @@ use std::borrow::Borrow;
use std::collections::{HashMap, HashSet};
use std::fmt::Display;
use std::net::IpAddr;
use tracing::{debug, warn};
use tracing::{debug, trace, warn};
pub use crate::node::{EntryDetails, RoutingNode, SupportedRoles};
pub use error::NymTopologyError;
@@ -293,7 +293,7 @@ impl NymTopology {
let has_exit_gateways = !self.rewarded_set.exit_gateways.is_empty();
let has_entry_gateways = !self.rewarded_set.entry_gateways.is_empty();
debug!(
trace!(
has_layer1 = %has_layer1,
has_layer2 = %has_layer2,
has_layer3 = %has_layer3,
@@ -401,20 +401,17 @@ impl NymTopology {
});
};
// a 'valid' egress is one assigned to either entry role (i.e. entry for another client)
// or exit role (as a service provider)
// a 'valid' egress is one that is currently **not** acting as a mixnode
if !ignore_epoch_roles {
let Some(role) = self.rewarded_set.role(node.node_id) else {
return Err(NymTopologyError::InvalidEgressRole {
node_identity: Box::new(node_identity),
});
};
if !matches!(role, Role::EntryGateway | Role::ExitGateway) {
return Err(NymTopologyError::InvalidEgressRole {
node_identity: Box::new(node_identity),
});
if let Some(role) = self.rewarded_set.role(node.node_id) {
if role.is_mixnode() {
return Err(NymTopologyError::InvalidEgressRole {
node_identity: Box::new(node_identity),
});
}
}
}
Ok(node)
}
+34 -1
View File
@@ -5,6 +5,7 @@
#![allow(clippy::drop_non_drop)]
use crate::error::WasmCoreError;
use nym_client_core::config::ForgetMe;
use nym_config::helpers::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
@@ -110,6 +111,8 @@ pub struct DebugWasm {
/// Defines all configuration options related to stats reporting.
#[wasm_bindgen(getter_with_clone)]
pub stats_reporting: StatsReportingWasm,
pub forget_me: ForgetMeWasm,
}
impl Default for DebugWasm {
@@ -128,6 +131,7 @@ impl From<DebugWasm> for ConfigDebug {
topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(),
stats_reporting: debug.stats_reporting.into(),
forget_me: debug.forget_me.into(),
}
}
}
@@ -142,6 +146,7 @@ impl From<ConfigDebug> for DebugWasm {
topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(),
stats_reporting: debug.stats_reporting.into(),
forget_me: ForgetMeWasm::from(debug.forget_me),
}
}
}
@@ -390,12 +395,15 @@ pub struct TopologyWasm {
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
/// Useless without `ignore_epoch_roles = true`
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool,
/// Specifies whether this client should ignore the current epoch role of the ingress node
/// when attempting to establish new connection
pub ignore_ingress_epoch_role: bool,
}
impl Default for TopologyWasm {
@@ -420,6 +428,7 @@ impl From<TopologyWasm> for ConfigTopology {
minimum_gateway_performance: topology.minimum_gateway_performance,
use_extended_topology: topology.use_extended_topology,
ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
}
}
}
@@ -437,6 +446,7 @@ impl From<ConfigTopology> for TopologyWasm {
minimum_gateway_performance: topology.minimum_gateway_performance,
use_extended_topology: topology.use_extended_topology,
ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
}
}
}
@@ -536,6 +546,29 @@ impl From<ConfigReplySurbs> for ReplySurbsWasm {
}
}
#[wasm_bindgen(inspectable)]
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, Copy, Default)]
#[serde(deny_unknown_fields)]
pub struct ForgetMeWasm {
pub client: bool,
pub stats: bool,
}
impl From<ForgetMeWasm> for ForgetMe {
fn from(value: ForgetMeWasm) -> Self {
ForgetMe::new(value.client, value.stats)
}
}
impl From<ForgetMe> for ForgetMeWasm {
fn from(value: ForgetMe) -> Self {
Self {
client: value.client(),
stats: value.stats(),
}
}
}
#[wasm_bindgen(inspectable)]
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
+34 -3
View File
@@ -8,8 +8,8 @@
#![allow(clippy::empty_docs)]
use super::{
AcknowledgementsWasm, CoverTrafficWasm, DebugWasm, GatewayConnectionWasm, ReplySurbsWasm,
StatsReportingWasm, TopologyWasm, TrafficWasm,
AcknowledgementsWasm, CoverTrafficWasm, DebugWasm, ForgetMeWasm, GatewayConnectionWasm,
ReplySurbsWasm, StatsReportingWasm, TopologyWasm, TrafficWasm,
};
use crate::config::ConfigDebug;
use serde::{Deserialize, Serialize};
@@ -47,6 +47,9 @@ pub struct DebugWasmOverride {
/// Defines all configuration options related to stats reporting.
#[tsify(optional)]
pub stats_reporting: Option<StatsReportingWasmOverride>,
#[tsify(optional)]
pub forget_me: Option<ForgetMeWasmOverride>,
}
impl From<DebugWasmOverride> for DebugWasm {
@@ -59,6 +62,7 @@ impl From<DebugWasmOverride> for DebugWasm {
topology: value.topology.map(Into::into).unwrap_or_default(),
reply_surbs: value.reply_surbs.map(Into::into).unwrap_or_default(),
stats_reporting: value.stats_reporting.map(Into::into).unwrap_or_default(),
forget_me: value.forget_me.map(Into::into).unwrap_or_default(),
}
}
}
@@ -274,7 +278,6 @@ pub struct TopologyWasmOverride {
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
/// Useless without `ignore_epoch_roles = true`
#[tsify(optional)]
pub use_extended_topology: Option<bool>,
@@ -282,6 +285,11 @@ pub struct TopologyWasmOverride {
/// when constructing the final hop packets.
#[tsify(optional)]
pub ignore_egress_epoch_role: Option<bool>,
/// Specifies whether this client should ignore the current epoch role of the ingress node
/// when attempting to establish new connection
#[tsify(optional)]
pub ignore_ingress_epoch_role: Option<bool>,
}
impl From<TopologyWasmOverride> for TopologyWasm {
@@ -311,6 +319,9 @@ impl From<TopologyWasmOverride> for TopologyWasm {
ignore_egress_epoch_role: value
.ignore_egress_epoch_role
.unwrap_or(def.ignore_egress_epoch_role),
ignore_ingress_epoch_role: value
.ignore_ingress_epoch_role
.unwrap_or(def.ignore_ingress_epoch_role),
}
}
}
@@ -401,6 +412,26 @@ impl From<ReplySurbsWasmOverride> for ReplySurbsWasm {
}
}
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[serde(rename_all = "camelCase")]
pub struct ForgetMeWasmOverride {
#[tsify(optional)]
pub client: Option<bool>,
#[tsify(optional)]
pub stats: Option<bool>,
}
impl From<ForgetMeWasmOverride> for ForgetMeWasm {
fn from(value: ForgetMeWasmOverride) -> Self {
ForgetMeWasm {
client: value.client.unwrap_or_default(),
stats: value.stats.unwrap_or_default(),
}
}
}
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[serde(rename_all = "camelCase")]
+28 -5
View File
@@ -10,7 +10,7 @@ use nym_client_core::client::base_client::storage::GatewaysDetailsStore;
use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::helpers::gateways_for_init;
use nym_client_core::init::types::GatewaySelectionSpecification;
use nym_client_core::init::{
self, setup_gateway,
@@ -132,9 +132,17 @@ pub async fn setup_gateway_from_api(
chosen_gateway: Option<IdentityKey>,
nym_apis: &[Url],
minimum_performance: u8,
ignore_epoch_roles: bool,
) -> Result<InitialisationResult, WasmCoreError> {
let mut rng = thread_rng();
let gateways = current_gateways(&mut rng, nym_apis, None, minimum_performance).await?;
let gateways = gateways_for_init(
&mut rng,
nym_apis,
None,
minimum_performance,
ignore_epoch_roles,
)
.await?;
setup_gateway_wasm(client_store, force_tls, chosen_gateway, gateways).await
}
@@ -142,9 +150,17 @@ pub async fn current_gateways_wasm(
nym_apis: &[Url],
user_agent: Option<UserAgent>,
minimum_performance: u8,
ignore_epoch_roles: bool,
) -> Result<Vec<RoutingNode>, ClientCoreError> {
let mut rng = thread_rng();
current_gateways(&mut rng, nym_apis, user_agent, minimum_performance).await
gateways_for_init(
&mut rng,
nym_apis,
user_agent,
minimum_performance,
ignore_epoch_roles,
)
.await
}
pub async fn setup_from_topology(
@@ -163,6 +179,7 @@ pub async fn generate_new_client_keys(store: &ClientStorage) -> Result<(), WasmC
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub async fn add_gateway(
preferred_gateway: Option<IdentityKey>,
latency_based_selection: Option<bool>,
@@ -170,6 +187,7 @@ pub async fn add_gateway(
nym_apis: &[Url],
user_agent: UserAgent,
min_performance: u8,
ignore_epoch_roles: bool,
storage: &ClientStorage,
) -> Result<(), WasmCoreError> {
let selection_spec = GatewaySelectionSpecification::new(
@@ -203,8 +221,13 @@ pub async fn add_gateway(
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let available_gateways =
current_gateways_wasm(nym_apis, Some(user_agent), min_performance).await?;
let available_gateways = current_gateways_wasm(
nym_apis,
Some(user_agent),
min_performance,
ignore_epoch_roles,
)
.await?;
// since we're registering with a brand new gateway,
// make sure the list of available gateways doesn't overlap the list of known gateways
+18 -15
View File
@@ -4,13 +4,15 @@
use crate::error::WasmCoreError;
use crate::storage::wasm_client_traits::{v1, v2, WasmClientStorage};
use async_trait::async_trait;
use js_sys::{Array, Promise};
use js_sys::Promise;
use serde::de::DeserializeOwned;
use serde::Serialize;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_storage::traits::BaseWasmStorage;
use wasm_storage::{IdbVersionChangeEvent, WasmStorage};
use wasm_storage::{
Build, Database, RawDbResult, TryFromJs, TryToJs, VersionChangeEvent, WasmStorage,
};
use wasm_utils::error::{simple_js_error, PromisableResult};
use zeroize::Zeroizing;
@@ -44,28 +46,29 @@ impl ClientStorage {
// special care must be taken on JS side to ensure it's correctly used there.
let passphrase = Zeroizing::new(passphrase);
let migrate_fn = Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
let migrate_fn = Some(|evt: VersionChangeEvent, db: Database| -> RawDbResult<()> {
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
// works with an unsigned integer.
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
let old_version = evt.old_version() as u32;
let db = evt.db();
if old_version < 1 {
// migrating to version 2
db.create_object_store(v1::KEYS_STORE)?;
db.create_object_store(v1::CORE_STORE)?;
db.create_object_store(v1::KEYS_STORE).build()?;
db.create_object_store(v1::CORE_STORE).build()?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE)?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE)
.build()?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)
.build()?;
return Ok(());
}
// version 1 -> unimplemented migration
if old_version < 2 {
return Err(simple_js_error("this client is incompatible with existing storage. please initialise it again."));
return Err(simple_js_error("this client is incompatible with existing storage. please initialise it again.").into());
}
Ok(())
@@ -112,7 +115,7 @@ impl BaseWasmStorage for ClientStorage {
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
where
T: DeserializeOwned,
K: JsCast,
K: TryToJs,
{
Ok(self.inner.read_value(store, key).await?)
}
@@ -125,33 +128,33 @@ impl BaseWasmStorage for ClientStorage {
) -> Result<(), Self::StorageError>
where
T: Serialize,
K: JsCast,
K: TryToJs + TryFromJs,
{
Ok(self.inner.store_value(store, key, value).await?)
}
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
where
K: JsCast,
K: TryToJs,
{
Ok(self.inner.remove_value(store, key).await?)
}
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
where
K: JsCast,
K: TryToJs,
{
Ok(self.inner.has_value(store, key).await?)
}
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
where
K: JsCast,
K: TryToJs,
{
Ok(self.inner.key_count(store, key).await?)
}
async fn get_all_keys(&self, store: &str) -> Result<Array, Self::StorageError> {
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError> {
Ok(self.inner.get_all_keys(store).await?)
}
}
@@ -286,8 +286,8 @@ pub trait WasmClientStorage: BaseWasmStorage {
.await
.map_err(Into::into)
.map(|arr| {
arr.to_vec()
.into_iter()
arr.iter()
.cloned()
.filter_map(|key| key.as_string())
.collect()
})
+1 -1
View File
@@ -9,7 +9,7 @@ license.workspace = true
[dependencies]
async-trait = { workspace = true }
futures = { workspace = true }
getrandom = { workspace = true, features = ["js"] }
js-sys = { workspace = true }
wasm-bindgen = { workspace = true }
serde = { workspace = true, features = ["derive"] }
+17 -16
View File
@@ -1,7 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use indexed_db_futures::web_sys::DomException;
use serde_wasm_bindgen::Error;
use thiserror::Error;
use wasm_bindgen::JsValue;
@@ -12,15 +11,11 @@ pub enum StorageError {
#[error("{0}")]
Json(String),
#[error("DomException {name} ({code}): {message}")]
DomException {
/// DomException code
code: u16,
/// Specific name of the DomException
name: String,
/// Message given to the DomException
message: String,
},
#[error("storage failure: {message}")]
InternalStorageFailure { message: String },
#[error("failed to open the db file: {message}")]
DbOpenFailure { message: String },
#[error("FATAL ERROR: storage key is somehow present {count} times in the table!")]
DuplicateKey { count: u32 },
@@ -46,12 +41,18 @@ impl From<StorageError> for JsValue {
}
}
impl From<DomException> for StorageError {
fn from(value: DomException) -> StorageError {
StorageError::DomException {
name: value.name(),
message: value.message(),
code: value.code(),
impl From<indexed_db_futures::error::Error> for StorageError {
fn from(value: indexed_db_futures::error::Error) -> Self {
StorageError::InternalStorageFailure {
message: value.to_string(),
}
}
}
impl From<indexed_db_futures::error::OpenDbError> for StorageError {
fn from(value: indexed_db_futures::error::OpenDbError) -> Self {
StorageError::DbOpenFailure {
message: value.to_string(),
}
}
}
+69 -52
View File
@@ -1,8 +1,9 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023-2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cipher_export::StoredExportedStoreCipher;
use crate::error::StorageError;
use indexed_db_futures::transaction::TransactionMode;
use nym_store_cipher::{
Aes256Gcm, Algorithm, EncryptedData, KdfInfo, KeySizeUser, Params, StoreCipher, Unsigned,
Version,
@@ -13,7 +14,10 @@ use std::future::IntoFuture;
use wasm_bindgen::JsValue;
use wasm_utils::console_log;
pub use indexed_db_futures::database::{Database, VersionChangeEvent};
pub use indexed_db_futures::prelude::*;
pub use indexed_db_futures::primitive::{TryFromJs, TryToJs};
pub use indexed_db_futures::Result as RawDbResult;
mod cipher_export;
pub mod error;
@@ -54,31 +58,29 @@ impl WasmStorage {
passphrase: Option<&[u8]>,
) -> Result<Self, StorageError>
where
F: Fn(&IdbVersionChangeEvent) -> Result<(), JsValue> + 'static,
F: Fn(VersionChangeEvent, Database) -> RawDbResult<()> + 'static,
{
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(db_name, version)?;
// we must always ensure the cipher table is present
db_req.set_on_upgrade_needed(Some(
move |evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
let db = Database::open(db_name)
.with_version(version)
.with_on_upgrade_needed(move |event, db| {
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
// works with an unsigned integer.
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
let old_version = evt.old_version() as u32;
let old_version = event.old_version() as u32;
if old_version < 1 {
evt.db().create_object_store(CIPHER_INFO_STORE)?;
db.create_object_store(CIPHER_INFO_STORE).build()?;
}
if let Some(migrate) = migrate_fn.as_ref() {
migrate(evt)
migrate(event, db)
} else {
Ok(())
}
},
));
})
.await?;
let db: IdbDatabase = db_req.into_future().await?;
let inner = IdbWrapper(db);
let store_cipher = inner.setup_store_cipher(passphrase).await?;
@@ -94,13 +96,12 @@ impl WasmStorage {
}
pub async fn remove(db_name: &str) -> Result<(), StorageError> {
IdbDatabase::delete_by_name(db_name)?.into_future().await?;
Database::delete_by_name(db_name)?.into_future().await?;
Ok(())
}
pub async fn exists(db_name: &str) -> Result<bool, StorageError> {
let db_req: OpenDbRequest = IdbDatabase::open(db_name)?;
let db: IdbDatabase = db_req.into_future().await?;
let db = Database::open(db_name).await?;
// if the db was already created before, at the very least cipher info store should exist,
// thus the iterator should return at least one value
@@ -139,7 +140,7 @@ impl WasmStorage {
pub async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, StorageError>
where
T: DeserializeOwned,
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.inner
.read_value_raw(store, key)
@@ -156,7 +157,7 @@ impl WasmStorage {
) -> Result<(), StorageError>
where
T: Serialize,
K: wasm_bindgen::JsCast,
K: TryToJs + TryFromJs,
{
self.inner
.store_value_raw(store, key, &self.serialize_value(&value)?)
@@ -165,14 +166,14 @@ impl WasmStorage {
pub async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.inner.remove_value_raw(store, key).await
}
pub async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
match self.key_count(store, key).await? {
0 => Ok(false),
@@ -183,82 +184,98 @@ impl WasmStorage {
pub async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.inner.get_key_count(store, key).await
}
pub async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, StorageError> {
pub async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, StorageError> {
self.inner.get_all_keys(store).await
}
}
struct IdbWrapper(IdbDatabase);
struct IdbWrapper(Database);
impl IdbWrapper {
async fn read_value_raw<K>(&self, store: &str, key: K) -> Result<Option<JsValue>, StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.0
.transaction_on_one_with_mode(store, IdbTransactionMode::Readonly)?
.transaction(store)
.with_mode(TransactionMode::Readonly)
.build()?
.object_store(store)?
.get(&key)?
.get(&key)
.primitive()?
.await
.map_err(Into::into)
}
async fn store_value_raw<K>(
async fn store_value_raw<K, T>(
&self,
store: &str,
key: K,
value: &JsValue,
value: &T,
) -> Result<(), StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs + TryFromJs,
T: TryToJs,
{
self.0
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
.object_store(store)?
.put_key_val_owned(key, value)?
.into_future()
.await
.map_err(Into::into)
let tx = self
.0
.transaction(store)
.with_mode(TransactionMode::Readwrite)
.build()?;
let store = tx.object_store(store)?;
store.put(value).with_key(key).primitive()?.await?;
tx.commit().await.map_err(Into::into)
}
async fn remove_value_raw<K>(&self, store: &str, key: K) -> Result<(), StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.0
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
.object_store(store)?
.delete_owned(key)?
.into_future()
.await
.map_err(Into::into)
let tx = self
.0
.transaction(store)
.with_mode(TransactionMode::Readwrite)
.build()?;
let store = tx.object_store(store)?;
store.delete(key).primitive()?.await?;
tx.commit().await.map_err(Into::into)
}
async fn get_key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.0
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
.transaction(store)
.with_mode(TransactionMode::Readonly)
.build()?
.object_store(store)?
.count_with_key_owned(key)?
.into_future()
.count()
.with_query(key)
.primitive()?
.await
.map_err(Into::into)
}
async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, StorageError> {
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, StorageError> {
self.0
.transaction_on_one_with_mode(store, IdbTransactionMode::Readonly)?
.transaction(store)
.with_mode(TransactionMode::Readonly)
.build()?
.object_store(store)?
.get_all_keys()?
.into_future()
.await
.get_all_keys()
.primitive()?
.await?
.collect::<Result<Vec<_>, _>>()
.map_err(Into::into)
}
+14 -13
View File
@@ -3,10 +3,11 @@
use crate::WasmStorage;
use async_trait::async_trait;
use js_sys::Array;
use indexed_db_futures::primitive::{TryFromJs, TryToJs};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::error::Error;
use wasm_bindgen::JsValue;
#[async_trait(?Send)]
pub trait BaseWasmStorage {
@@ -17,7 +18,7 @@ pub trait BaseWasmStorage {
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
where
T: DeserializeOwned,
K: wasm_bindgen::JsCast;
K: TryToJs;
async fn store_value<T, K>(
&self,
@@ -27,21 +28,21 @@ pub trait BaseWasmStorage {
) -> Result<(), Self::StorageError>
where
T: Serialize,
K: wasm_bindgen::JsCast;
K: TryToJs + TryFromJs;
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
where
K: wasm_bindgen::JsCast;
K: TryToJs;
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
where
K: wasm_bindgen::JsCast;
K: TryToJs;
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
where
K: wasm_bindgen::JsCast;
K: TryToJs;
async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, Self::StorageError>;
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError>;
}
#[async_trait(?Send)]
@@ -55,7 +56,7 @@ impl BaseWasmStorage for WasmStorage {
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
where
T: DeserializeOwned,
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.read_value(store, key).await
}
@@ -68,33 +69,33 @@ impl BaseWasmStorage for WasmStorage {
) -> Result<(), Self::StorageError>
where
T: Serialize,
K: wasm_bindgen::JsCast,
K: TryToJs + TryFromJs,
{
self.store_value(store, key, value).await
}
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.remove_value(store, key).await
}
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.has_value(store, key).await
}
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
where
K: wasm_bindgen::JsCast,
K: TryToJs,
{
self.key_count(store, key).await
}
async fn get_all_keys(&self, store: &str) -> Result<Array, Self::StorageError> {
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError> {
self.get_all_keys(store).await
}
}
+59 -39
View File
@@ -179,7 +179,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -233,7 +233,7 @@ dependencies = [
"schemars",
"serde",
"subtle-encoding",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -267,7 +267,7 @@ dependencies = [
"ed25519-zebra",
"k256 0.13.3",
"rand_core 0.6.4",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -289,7 +289,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -320,7 +320,7 @@ dependencies = [
"serde",
"serde-json-wasm",
"sha2 0.10.8",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -433,7 +433,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.85",
"syn 2.0.98",
]
[[package]]
@@ -448,7 +448,7 @@ dependencies = [
"cw-utils",
"schemars",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -467,7 +467,7 @@ dependencies = [
"prost",
"schemars",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -493,7 +493,7 @@ dependencies = [
"schemars",
"semver",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -508,7 +508,7 @@ dependencies = [
"schemars",
"semver",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -538,7 +538,7 @@ dependencies = [
"schemars",
"semver",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -553,7 +553,7 @@ dependencies = [
"cw20",
"schemars",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -570,7 +570,7 @@ dependencies = [
"cw3",
"schemars",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -622,7 +622,7 @@ dependencies = [
"nym-group-contract-common",
"schemars",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -1140,7 +1140,7 @@ dependencies = [
"nym-coconut-bandwidth-contract-common",
"nym-multisig-contract-common",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -1170,7 +1170,7 @@ dependencies = [
"nym-contracts-common",
"nym-group-contract-common",
"serde",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -1196,7 +1196,7 @@ dependencies = [
"cw-storage-plus",
"schemars",
"serde",
"thiserror",
"thiserror 2.0.11",
"vergen",
]
@@ -1210,7 +1210,7 @@ dependencies = [
"nym-sphinx-types",
"rand",
"subtle-encoding",
"thiserror",
"thiserror 2.0.11",
"x25519-dalek",
"zeroize",
]
@@ -1240,7 +1240,7 @@ dependencies = [
"semver",
"serde",
"sylvia",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -1254,7 +1254,7 @@ dependencies = [
"cw-utils",
"cw2",
"nym-multisig-contract-common",
"thiserror",
"thiserror 2.0.11",
]
[[package]]
@@ -1289,7 +1289,7 @@ dependencies = [
"rand_chacha",
"semver",
"serde",
"thiserror",
"thiserror 1.0.64",
"time",
]
@@ -1310,7 +1310,7 @@ dependencies = [
"serde",
"serde-json-wasm",
"serde_repr",
"thiserror",
"thiserror 2.0.11",
"time",
]
@@ -1326,7 +1326,7 @@ dependencies = [
"cw4",
"schemars",
"serde",
"thiserror",
"thiserror 2.0.11",
]
[[package]]
@@ -1349,7 +1349,7 @@ name = "nym-sphinx-types"
version = "0.2.0"
dependencies = [
"sphinx-packet",
"thiserror",
"thiserror 2.0.11",
]
[[package]]
@@ -1370,7 +1370,7 @@ dependencies = [
"rand_chacha",
"serde",
"serde_json",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
@@ -1383,7 +1383,7 @@ dependencies = [
"nym-contracts-common",
"nym-mixnet-contract-common",
"serde",
"thiserror",
"thiserror 2.0.11",
]
[[package]]
@@ -1489,9 +1489,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.89"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
@@ -1666,7 +1666,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.85",
"syn 2.0.98",
]
[[package]]
@@ -1699,9 +1699,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.24"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
dependencies = [
"serde",
]
@@ -1741,7 +1741,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.85",
"syn 2.0.98",
]
[[package]]
@@ -1752,7 +1752,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.85",
"syn 2.0.98",
]
[[package]]
@@ -1775,7 +1775,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.85",
"syn 2.0.98",
]
[[package]]
@@ -1934,9 +1934,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.85"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
@@ -1949,7 +1949,16 @@ version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
"thiserror-impl 1.0.64",
]
[[package]]
name = "thiserror"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [
"thiserror-impl 2.0.11",
]
[[package]]
@@ -1960,7 +1969,18 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.85",
"syn 2.0.98",
]
[[package]]
name = "thiserror-impl"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
@@ -2126,5 +2146,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.85",
"syn 2.0.98",
]
@@ -163,3 +163,4 @@ Another command is `version_count` where at least one `nym-node` version is requ
```sh
./node_api_check version_count 1.1.0 1.1.1 1.1.2 1.1.3 --markdown
```
@@ -10,7 +10,7 @@ import Stack from '@mui/material/Stack';
import Paper from '@mui/material/Paper';
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
const defaultUrl = 'https://nymtech.net/favicon.svg';
const defaultUrl = 'https://nym.com/favicon.svg';
const args = { mode: 'unsafe-ignore-cors' };
const mixFetchOptions: SetupMixFetchOps = {
@@ -11,22 +11,38 @@ Tor community created a very helpful table called [*Good Bad ISPs*](https://comm
<div>
<Tabs items={[
<code>nym-node</code>,
<><code>nym-node</code> - dedicated server</>,
<><code>nym-node</code> - VPS</>,
<code>validator</code>,
]} defaultIndex="0">
<MyTab>
#### `nym-node`
Before we conclude the testing with exact results, these are the rough specs:
#### `nym-node` - dedicated server
If you are about to start a new Nym Node, look for a server with this specification:
| **Hardware** | **Minimum Specification** |
| :--- | ---: |
| CPU Cores | 4 |
| Memory | 8 GB RAM |
| Storage | 80 GB |
| Connectivity | IPv4, IPv6, TCP/IP, UDP |
| Bandwidth | > 1Tb |
| Port speed | 1Gbps |
| **Hardware** | **Minimum Specification** |
| :--- | ---: |
| Type of server | Dedicated or bare metal |
| CPU Cores | 4 |
| Memory | 8-16 GB RAM |
| Storage | 50 GB (preferably 80 GB) |
| Connectivity | IPv4, IPv6, TCP/IP, UDP |
| Bandwidth | Unmetered if possible, or \> 40Tb |
| Port speed | 1Gbps |
</MyTab>
<MyTab>
#### `nym-node` - VPS
If you are about to start a new Nym Node, look for a server with this specification:
| **Hardware** | **Minimum Specification** |
| :--- | ---: |
| Type of server | VPS |
| CPU Cores | 4 |
| Memory | 8-16 GB RAM |
| Storage | 50 GB (preferably 80 GB) |
| Connectivity | IPv4, IPv6, TCP/IP, UDP |
| Bandwidth | Unmetered if possible, or \> 40Tb |
| Port speed | 10Gbps |
</MyTab>
<MyTab>
#### Nyx validator
@@ -0,0 +1,9 @@
import { Callout } from 'nextra/components';
### Terms & Conditions
<Callout type="info" emoji="️">
From `nym-node` version `1.1.3` onward is required to accept [**Operators Terms & Conditions**](https://nymtech.net/terms-and-conditions/operators/v1.0.0) in order to be part of the active set. Make sure to read them before you add the flag.
</Callout>
There has been a long ongoing discussion whether and how to apply Terms and Conditions for Nym network operators, with an aim to stay aligned with the philosophy of Free Software and provide legal defense for both node operators and Nym developers. To understand better the reasoning behind this decision, you can listen to the first [Nym Operator Town Hall](https://www.youtube.com/live/7hwb8bAZIuc?si=3mQ2ed7AyUA1SsCp&t=915) introducing the T&Cs or to [Operator AMA with CEO Harry Halpin](https://www.youtube.com/watch?v=yIN-zYQw0I0) from June 4th, 2024, explaining pros and cons of T&Cs implementation.
@@ -0,0 +1,25 @@
```
Dear <ISP>:
Thank you for forwarding me the notice you received from <COPYRIGHT_CLAIMANT> regarding <CONTENT>. I would like to assure you that I am not hosting the claimed infringing materials, and I believe that the notice is likely based upon misunderstandings about the law and about some of the software I run. I believe that the Digital Millennium Copyright Act's ("DMCA") safe harbor provisions likely protect you from liability arising from this complaint.
As you know, the DMCA creates four "safe harbors" for service providers (such as ISPs) to protect them from copyright liability for the acts of their users, when the service provider fulfill certain requirements. (See 17 U.S.C. 512). The requirements to meet the DMCA safe harbor provisions vary depending on the type of safe harbor claimed.
You may be familiar with the "notice and takedown" requirements of section 512(c) of the DMCA, which require a service provider respond to expeditiously to remove, or disable access to, the material that is claimed to be infringing or to be the subject of infringing activity. However, we believe that the more appropriate safe harbor provision is under section 512(a), which applies when the service provider merely acts as a conduit. In such case, there are different and less burdensome eligibility requirements, as the D.C. Circuit Court of Appeals held in RIAA v. Verizon (see https://scholar.google.com/scholar_case?case=15815830240179540527) and the Eighth Circuit Court of Appeals confirmed in RIAA v. Charter (see https://scholar.google.com/scholar_case?case=11547531128234336420).
Under DMCA 512(a), service providers like you are typically protected from damages for copyright infringement claims if you also maintain "a policy that provides for termination in appropriate circumstances of subscribers and account holders of the service provider's system or network who are repeat infringers." If you have and implement such a policy, and you otherwise qualify for the safe harbor, you should be free from fear of copyright damages.
In this case, the copyright notice you received was likely triggered by a program I run called Nym. Nym is a network software that helps users to enhance their privacy, security, and safety online.
The program does not host any content. Rather, it is part of a network of nodes on the Internet that simply pass packets among themselves before sending them to their destinations, just as any Internet intermediary does. The difference is that Nym tunnels the connections such that no intermediary can learn both the source and destination of the packets, giving users protection from nefarious snooping on network traffic. The result is that, unlike most other Internet traffic, the final IP address that the recipient receives is not the IP address of the sender. Nym protects users against hazards such as harassment, spam, and identity theft.
Nym aims to improve on technology developed by Panoramix by building a decentralized authentication and payment protocol. It will enable developers to build their own sustainable privacy-enhanced services. Panoramix is an EU-funded Horizon 2020 programme with the goal of protecting communication privacy by building a comprehensive mixnet infrastructure. This project has received funding from the European Unions Horizon 2020 research and innovation programme under the Grant Agreement No 653497, "Privacy and Accountability in Networks via Optimized Randomized Mix-nets (Panoramix)” (For more on Nym, see https://www.nym.com/, For more on Panoramix, see https://panoramix.me/ .) I hope, as an organization committed to protecting the privacy of its customers, you'll agree that this is a valuable technology.
While the Nym node that I run may appear to be the source of material that is alleged to be infringing, I do not host that material. I do not select the material transmitted through the Nym node that I run, and I have no practical means of either identifying the source of such material or preventing its transmission. In addition, I do nothing to encourage or promote the use of the Nym network for copyright infringement or other prohibited activities. For these reasons, I am not an infringer of copyright in any materials that are transmitted through the Nym node that I run, either directly or under a theory of contributory or vicarious liability. In addition, as you are just acting as a conduit, you should continue to be protected under the DMCA 512(a) safe harbor provision without taking any further action.
Thank you for working with me on this matter. As a loyal subscriber, I appreciate your notifying me of this issue and hope that the protections of DMCA 512 put any concerns you may have to rest. If not, please contact me with any further questions.
Very truly yours,
Your customer, <YOUR_NAME/PSEUDONYM>
```
@@ -0,0 +1,19 @@
```
Hi,
I am reaching out to introduce myself! I am about to spin up a machine with you to run what is called a “nym node” - think of it as somewhat similar to a Tor exit node.
You can always recognize a nym node by our domain names: nym-exit
Nym node runners are a decentralized community all over the world. We provide secure internet traffic routing services to ordinary people and businesses via the Nym platform and NymVPN app.
The Nym platform implements strict encryption and security standards, which also means I simply relay traffic and do not know the end-destination nor its content.
The Nym traffic pattern is somewhat unique, as we route traffic using the “sphinx” packet format (again, think onion routing), which makes all traffic look uniform. Id like to ensure this unique traffic pattern doesnt raise any flags or issues with you! See https://nym.com/ for more details.
Feel free to ask any questions.
Many thanks,
<YOUR_NAME/PSEUDONYM>
```
@@ -1 +1 @@
805_903_308
807_251_217
@@ -1 +1 @@
1_022_821
1_025_628
@@ -1 +1 @@
402_951_654
403_625_608
@@ -1,7 +1,7 @@
| **Item** | **Description** | **Amount in NYM** |
|:-------------------|:------------------------------------------------------|--------------------:|
| Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 |
| Mixmining Reserve | Tokens releasing for operators rewards | 194_096_191 |
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 500 |
| Circulating Supply | Amount of unlocked tokens | 805_903_308 |
| Stake Saturation | Optimal size of node self-bond + delegation | 1_022_821 |
| Mixmining Reserve | Tokens releasing for operators rewards | 192_748_782 |
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 0 |
| Circulating Supply | Amount of unlocked tokens | 807_251_217 |
| Stake Saturation | Optimal size of node self-bond + delegation | 1_025_628 |
@@ -1 +1 @@
Wednesday, January 8th 2025, 15:01:28 UTC
Monday, February 3rd 2025, 13:47:19 UTC
+45 -14
View File
@@ -497,13 +497,6 @@ const config = {
permanent: true,
basePath: false,
},
{
source: "/operators/testing/node-api-check.html",
destination:
"/docs/operators/nodes/performance-and-testing/node-api-check",
permanent: true,
basePath: false,
},
{
source: "/operators/testing/prometheus-grafana.html",
destination:
@@ -663,13 +656,6 @@ const config = {
permanent: true,
basePath: false,
},
{
source: "/docs/operators/testing/node-api-check",
destination:
"/docs/operators/nodes/performance-and-testing/node-api-check",
permanent: true,
basePath: false,
},
{
source: "/docs/operators/nodes/proxy-configuration",
destination:
@@ -1081,6 +1067,51 @@ const config = {
unoptimized: true,
},
transpilePackages: ["@nymproject/contract-clients"],
async headers() {
const isDev = process.env.NODE_ENV === "development";
const csp = isDev
? `
default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: *;
script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: *;
font-src 'self' data: blob: *;
style-src 'self' 'unsafe-inline' data: blob: *;
img-src 'self' data: blob: *;
object-src 'self' data: blob: *;
base-uri 'self';
form-action 'self';
frame-ancestors 'self';
upgrade-insecure-requests;
connect-src 'self' data: blob: *;
frame-src 'self' data: blob: *;
worker-src 'self' blob: *;
`
: `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://vercel.live *.nymtech.net *.nymvpn.com *.vercel.app *.nymte.ch *.nyx.network *.nym.com https://nym.com nymvpn.com https://nymvpn.com *.nymtech.cc;
font-src 'self' data:;
style-src 'self' 'unsafe-inline';
img-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
connect-src 'self' https://github.com *.vercel.app *.nymtech.net *.nymvpn.com *.nymte.ch *.nyx.network *.nym.com https://nym.com nymvpn.com https://nymvpn.com *.nymtech.cc;
frame-src 'self' https://vercel.live *.vercel.app *.nym.com https://nym.com;
worker-src 'self' blob: https://vercel.live *.vercel.app *.nym.com https://nym.com;
`;
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: csp.replace(/\s{2,}/g, " ").trim(),
}
]
}
]
}
};
module.exports = config;
@@ -4,6 +4,8 @@ The information below is generated with [Redoc](https://redocly.com/docs/redoc)
There is also an overview of other Validator endpoints at [https://cosmos.directory/nyx](https://cosmos.directory/nyx).
<br /><br />
<RedocStandalone
specUrl="https://api.nymtech.net/swagger/swagger.yaml"
options={{
@@ -1,9 +1,11 @@
import { RedocStandalone } from 'redoc';
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at []() which is also used to generate the Swagger docs deployed at []().
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://api.sandbox.nymtech.net/swagger/swagger.yaml](https://api.sandbox.nymtech.net/swagger/swagger.yaml).
<br /><br />
<RedocStandalone
specUrl=""
specUrl="https://api.sandbox.nymtech.net/swagger/swagger.yaml"
options={{
nativeScrollbars: true,
theme: {
@@ -3,6 +3,6 @@
The Explorer API is the backend for the [Mixnet Explorer](https://explorer.nymtech.net/).
**This will soon be deprecated in favour of the [Node Status API](../ns-api).**
**This will soon be deprecated in favour of the [Node Status API](ns-api.mdx).**
The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/explorer).
@@ -3,6 +3,8 @@ import { RedocStandalone } from 'redoc';
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://explorer.nymtech.net/api/v1/openapi.json](https://explorer.nymtech.net/api/v1/openapi.json) which is also used to generate the Swagger docs deployed at [https://explorer.nymtech.net/api/swagger/index.html](https://explorer.nymtech.net/api/swagger/index.html).
<br /><br />
<RedocStandalone
specUrl="https://sandbox-explorer.nymtech.net/api/v1/openapi.json"
options={{
@@ -2,6 +2,8 @@ import { RedocStandalone } from 'redoc';
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://sandbox-explorer.nymtech.net/api/v1/openapi.json](https://sandbox-explorer.nymtech.net/api/v1/openapi.json) which is also used to generate the Swagger docs deployed at [https://sandbox-explorer.nymtech.net/api/swagger/index.html](https://sandbox-explorer.nymtech.net/api/swagger/index.html).
<br /><br />
<RedocStandalone
specUrl="https://sandbox-explorer.nymtech.net/api/v1/openapi.json"
options={{
@@ -2,6 +2,8 @@ import { RedocStandalone } from 'redoc';
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json](https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://mainnet-node-status-api.nymtech.cc/swagger/](https://mainnet-node-status-api.nymtech.cc/swagger/).
<br /><br />
<RedocStandalone
specUrl="https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json"
options={{

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